fs-err-2.11.0/.cargo_vcs_info.json0000644000000001360000000000100123130ustar { "git": { "sha1": "ba9888793c517a82499c8df1ad013529d8578777" }, "path_in_vcs": "" }fs-err-2.11.0/CHANGELOG.md000064400000000000000000000076200072674642500127510ustar 00000000000000# fs-err Changelog ## 2.11.0 * Added the first line of the standard library documentation to each function's rustdocs, to make them more useful in IDEs ([#50](https://github.com/andrewhickman/fs-err/issues/45)) * Fixed the wrapper for `tokio::fs::symlink_dir()` on Windows being incorrectly named `symlink`. The old function is now deprecated and will be removed in the next breaking release. ## 2.10.0 * Add `fs_err_try_exists` to `std::path::Path` via extension trait. This feature requires Rust 1.63 or later. ([#48](https://github.com/andrewhickman/fs-err/pull/48)) ## 2.9.0 * Add wrappers for [`tokio::fs`](https://docs.rs/tokio/latest/tokio/fs/index.html) ([#40](https://github.com/andrewhickman/fs-err/pull/40)). ## 2.8.1 * Fixed docs.rs build ## 2.8.0 * Implement I/O safety traits (`AsFd`/`AsHandle`, `Into`/`Into`) for file. This feature requires Rust 1.63 or later and is gated behind the `io_safety` feature flag. ([#39](https://github.com/andrewhickman/fs-err/pull/39)) ## 2.7.0 * Implement `From for std::fs::File` ([#38](https://github.com/andrewhickman/fs-err/pull/38)) ## 2.6.0 * Added [`File::into_parts`](https://docs.rs/fs-err/2.6.0/fs_err/struct.File.html#method.into_parts) and [`File::file_mut`](https://docs.rs/fs-err/2.6.0/fs_err/struct.File.html#method.file_mut) to provide more access to the underlying `std::fs::File`. * Fixed some typos in documention ([#33](https://github.com/andrewhickman/fs-err/pull/33)) ## 2.5.0 * Added `symlink` for unix platforms * Added `symlink_file` and `symlink_dir` for windows * Implemented os-specific extension traits for `File` - `std::os::unix::io::{AsRawFd, IntoRawFd}` - `std::os::windows::io::{AsRawHandle, IntoRawHandle}` - Added trait wrappers for `std::os::{unix, windows}::fs::FileExt` and implemented them for `fs_err::File` * Implemented os-specific extension traits for `OpenOptions` - Added trait wrappers for `std::os::{unix, windows}::fs::OpenOptionsExt` and implemented them for `fs_err::OpenOptions` * Improved compile times by converting arguments early and forwarding only a small number of types internally. There will be a slight performance hit only in the error case. * Reduced trait bounds on generics from `AsRef + Into` to either `AsRef` or `Into`, making the functions more general. ## 2.4.0 * Added `canonicalize`, `hard link`, `read_link`, `rename`, `symlink_metadata` and `soft_link`. ([#25](https://github.com/andrewhickman/fs-err/pull/25)) * Added aliases to `std::path::Path` via extension trait ([#26](https://github.com/andrewhickman/fs-err/pull/26)) * Added `OpenOptions` ([#27](https://github.com/andrewhickman/fs-err/pull/27)) * Added `set_permissions` ([#28](https://github.com/andrewhickman/fs-err/pull/28)) ## 2.3.0 * Added `create_dir` and `create_dir_all`. ([#19](https://github.com/andrewhickman/fs-err/pull/19)) * Added `remove_file`, `remove_dir`, and `remove_dir_all`. ([#16](https://github.com/andrewhickman/fs-err/pull/16)) ## 2.2.0 * Added `metadata`. ([#15](https://github.com/andrewhickman/fs-err/pull/15)) ## 2.1.0 * Updated crate-level documentation. ([#8](https://github.com/andrewhickman/fs-err/pull/8)) * Added `read_dir`, `ReadDir`, and `DirEntry`. ([#9](https://github.com/andrewhickman/fs-err/pull/9)) ## 2.0.1 (2020-02-22) * Added `copy`. ([#7](https://github.com/andrewhickman/fs-err/pull/7)) ## 2.0.0 (2020-02-19) * Removed custom error type in favor of `std::io::Error`. ([#2](https://github.com/andrewhickman/fs-err/pull/2)) ## 1.0.1 (2020-02-15) * Fixed bad documentation link in `Cargo.toml`. ## 1.0.0 (2020-02-15) * No changes from 0.1.2. ## 0.1.2 (2020-02-10) * Added `Error::cause` implementation for `fs_err::Error`. ## 0.1.1 (2020-02-05) * Added wrappers for `std::fs::*` functions. ## 0.1.0 (2020-02-02) * Initial release, containing a wrapper around `std::fs::File`. fs-err-2.11.0/Cargo.toml0000644000000027770000000000100103260ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2018" name = "fs-err" version = "2.11.0" authors = ["Andrew Hickman "] exclude = [ ".github", ".gitignore", "README.tpl", ] description = "A drop-in replacement for std::fs with more helpful error messages." documentation = "https://docs.rs/fs-err" readme = "README.md" categories = [ "command-line-interface", "filesystem", ] license = "MIT/Apache-2.0" repository = "https://github.com/andrewhickman/fs-err" [package.metadata.docs.rs] all-features = true rustdoc-args = [ "--cfg", "docsrs", ] [package.metadata.release] sign-tag = true tag-name = "{{version}}" [[package.metadata.release.pre-release-replacements]] exactly = 1 file = "src/lib.rs" replace = "html_root_url = \"https://docs.rs/fs-err/{{version}}\"" search = 'html_root_url = "https://docs\.rs/fs-err/.*?"' [dependencies.tokio] version = "1.21" features = ["fs"] optional = true default_features = false [dev-dependencies.serde_json] version = "1.0.64" [build-dependencies.autocfg] version = "1" [features] io_safety = [] fs-err-2.11.0/Cargo.toml.orig000064400000000000000000000020650072674642500140250ustar 00000000000000[package] name = "fs-err" description = "A drop-in replacement for std::fs with more helpful error messages." version = "2.11.0" authors = ["Andrew Hickman "] edition = "2018" repository = "https://github.com/andrewhickman/fs-err" documentation = "https://docs.rs/fs-err" categories = ["command-line-interface", "filesystem"] license = "MIT/Apache-2.0" readme = "README.md" exclude = [".github", ".gitignore", "README.tpl"] [dependencies] tokio = { version = "1.21", optional = true, default_features = false, features = ["fs"] } [build-dependencies] autocfg = "1" [dev-dependencies] serde_json = "1.0.64" [features] # Adds I/O safety traits, introduced in Rust 1.63 io_safety = [] [package.metadata.release] tag-name = "{{version}}" sign-tag = true [[package.metadata.release.pre-release-replacements]] file = "src/lib.rs" search = "html_root_url = \"https://docs\\.rs/fs-err/.*?\"" replace = "html_root_url = \"https://docs.rs/fs-err/{{version}}\"" exactly = 1 [package.metadata.docs.rs] all-features = true rustdoc-args = ["--cfg", "docsrs"] fs-err-2.11.0/LICENSE-APACHE000064400000000000000000000254500072674642500130650ustar 00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. fs-err-2.11.0/LICENSE-MIT000064400000000000000000000020260072674642500125670ustar 00000000000000Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. fs-err-2.11.0/README.md000064400000000000000000000060330072674642500124140ustar 00000000000000 # fs-err [![Crates.io](https://img.shields.io/crates/v/fs-err.svg)](https://crates.io/crates/fs-err) [![GitHub Actions](https://github.com/andrewhickman/fs-err/workflows/CI/badge.svg)](https://github.com/andrewhickman/fs-err/actions?query=workflow%3ACI) fs-err is a drop-in replacement for [`std::fs`][std::fs] that provides more helpful messages on errors. Extra information includes which operations was attempted and any involved paths. ## Error Messages Using [`std::fs`][std::fs], if this code fails: ```rust let file = File::open("does not exist.txt")?; ``` The error message that Rust gives you isn't very useful: ```txt The system cannot find the file specified. (os error 2) ``` ...but if we use fs-err instead, our error contains more actionable information: ```txt failed to open file `does not exist.txt` caused by: The system cannot find the file specified. (os error 2) ``` ## Usage fs-err's API is the same as [`std::fs`][std::fs], so migrating code to use it is easy. ```rust // use std::fs; use fs_err as fs; let contents = fs::read_to_string("foo.txt")?; println!("Read foo.txt: {}", contents); ``` fs-err uses [`std::io::Error`][std::io::Error] for all errors. This helps fs-err compose well with traits from the standard library like [`std::io::Read`][std::io::Read] and crates that use them like [`serde_json`][serde_json]: ```rust use fs_err::File; let file = File::open("my-config.json")?; // If an I/O error occurs inside serde_json, the error will include a file path // as well as what operation was being performed. let decoded: Vec = serde_json::from_reader(file)?; println!("Program config: {:?}", decoded); ``` [std::fs]: https://doc.rust-lang.org/stable/std/fs/ [std::io::Error]: https://doc.rust-lang.org/stable/std/io/struct.Error.html [std::io::Read]: https://doc.rust-lang.org/stable/std/io/trait.Read.html [serde_json]: https://crates.io/crates/serde_json ## Minimum Supported Rust Version The oldest rust version this crate is tested on is **1.40**. This crate will generally be conservative with rust version updates. It uses the [`autocfg`](https://crates.io/crates/autocfg) crate to allow wrapping new APIs without incrementing the MSRV. If the `tokio` feature is enabled, this crate will inherit the MSRV of the selected [`tokio`](https://crates.io/crates/tokio) version. ## License Licensed under either of * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or https://www.apache.org/licenses/LICENSE-2.0) * MIT license ([LICENSE-MIT](LICENSE-MIT) or https://opensource.org/licenses/MIT) at your option. ### Contribution Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. fs-err-2.11.0/build.rs000064400000000000000000000002410072674642500125750ustar 00000000000000extern crate autocfg; fn main() { let ac = autocfg::new(); // Allows `#[cfg(rustc_1_63)]` to be used in code ac.emit_rustc_version(1, 63); } fs-err-2.11.0/src/dir.rs000064400000000000000000000062150072674642500130520ustar 00000000000000use std::ffi::OsString; use std::fs; use std::io; use std::path::PathBuf; use crate::errors::{Error, ErrorKind}; /// Returns an iterator over the entries within a directory. /// /// Wrapper for [`fs::read_dir`](https://doc.rust-lang.org/stable/std/fs/fn.read_dir.html). pub fn read_dir>(path: P) -> io::Result { let path = path.into(); match fs::read_dir(&path) { Ok(inner) => Ok(ReadDir { inner, path }), Err(source) => Err(Error::build(source, ErrorKind::ReadDir, path)), } } /// Wrapper around [`std::fs::ReadDir`][std::fs::ReadDir] which adds more /// helpful information to all errors. /// /// This struct is created via [`fs_err::read_dir`][fs_err::read_dir]. /// /// [std::fs::ReadDir]: https://doc.rust-lang.org/stable/std/fs/struct.ReadDir.html /// [fs_err::read_dir]: fn.read_dir.html #[derive(Debug)] pub struct ReadDir { inner: fs::ReadDir, path: PathBuf, } impl Iterator for ReadDir { type Item = io::Result; fn next(&mut self) -> Option { Some( self.inner .next()? .map_err(|source| Error::build(source, ErrorKind::ReadDir, &self.path)) .map(|inner| DirEntry { inner }), ) } } /// Wrapper around [`std::fs::DirEntry`][std::fs::DirEntry] which adds more /// helpful information to all errors. /// /// [std::fs::DirEntry]: https://doc.rust-lang.org/stable/std/fs/struct.DirEntry.html #[derive(Debug)] pub struct DirEntry { inner: fs::DirEntry, } impl DirEntry { /// Returns the full path to the file that this entry represents. /// /// Wrapper for [`DirEntry::path`](https://doc.rust-lang.org/stable/std/fs/struct.DirEntry.html#method.path). pub fn path(&self) -> PathBuf { self.inner.path() } /// Returns the metadata for the file that this entry points at. /// /// Wrapper for [`DirEntry::metadata`](https://doc.rust-lang.org/stable/std/fs/struct.DirEntry.html#method.metadata). pub fn metadata(&self) -> io::Result { self.inner .metadata() .map_err(|source| Error::build(source, ErrorKind::Metadata, self.path())) } /// Returns the file type for the file that this entry points at. /// /// Wrapper for [`DirEntry::file_type`](https://doc.rust-lang.org/stable/std/fs/struct.DirEntry.html#method.file_type). pub fn file_type(&self) -> io::Result { self.inner .file_type() .map_err(|source| Error::build(source, ErrorKind::Metadata, self.path())) } /// Returns the file name of this directory entry without any leading path component(s). /// /// Wrapper for [`DirEntry::file_name`](https://doc.rust-lang.org/stable/std/fs/struct.DirEntry.html#method.file_name). pub fn file_name(&self) -> OsString { self.inner.file_name() } } #[cfg(unix)] mod unix { use std::os::unix::fs::DirEntryExt; use super::*; impl DirEntryExt for DirEntry { fn ino(&self) -> u64 { self.inner.ino() } } } fs-err-2.11.0/src/errors.rs000064400000000000000000000141130072674642500136040ustar 00000000000000use std::error::Error as StdError; use std::fmt; use std::io; use std::path::PathBuf; #[derive(Debug, Clone, Copy)] pub(crate) enum ErrorKind { OpenFile, CreateFile, CreateDir, SyncFile, SetLen, Metadata, Clone, SetPermissions, Read, Seek, Write, Flush, ReadDir, RemoveFile, RemoveDir, Canonicalize, ReadLink, SymlinkMetadata, #[allow(dead_code)] FileExists, #[cfg(windows)] SeekRead, #[cfg(windows)] SeekWrite, #[cfg(unix)] ReadAt, #[cfg(unix)] WriteAt, } /// Contains an IO error that has a file path attached. /// /// This type is never returned directly, but is instead wrapped inside yet /// another IO error. #[derive(Debug)] pub(crate) struct Error { kind: ErrorKind, source: io::Error, path: PathBuf, } impl Error { pub fn build(source: io::Error, kind: ErrorKind, path: impl Into) -> io::Error { io::Error::new( source.kind(), Self { kind, source, path: path.into(), }, ) } } impl fmt::Display for Error { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { use ErrorKind::*; let path = self.path.display(); match self.kind { OpenFile => write!(formatter, "failed to open file `{}`", path), CreateFile => write!(formatter, "failed to create file `{}`", path), CreateDir => write!(formatter, "failed to create directory `{}`", path), SyncFile => write!(formatter, "failed to sync file `{}`", path), SetLen => write!(formatter, "failed to set length of file `{}`", path), Metadata => write!(formatter, "failed to query metadata of file `{}`", path), Clone => write!(formatter, "failed to clone handle for file `{}`", path), SetPermissions => write!(formatter, "failed to set permissions for file `{}`", path), Read => write!(formatter, "failed to read from file `{}`", path), Seek => write!(formatter, "failed to seek in file `{}`", path), Write => write!(formatter, "failed to write to file `{}`", path), Flush => write!(formatter, "failed to flush file `{}`", path), ReadDir => write!(formatter, "failed to read directory `{}`", path), RemoveFile => write!(formatter, "failed to remove file `{}`", path), RemoveDir => write!(formatter, "failed to remove directory `{}`", path), Canonicalize => write!(formatter, "failed to canonicalize path `{}`", path), ReadLink => write!(formatter, "failed to read symbolic link `{}`", path), SymlinkMetadata => write!(formatter, "failed to query metadata of symlink `{}`", path), FileExists => write!(formatter, "failed to check file existance `{}`", path), #[cfg(windows)] SeekRead => write!(formatter, "failed to seek and read from `{}`", path), #[cfg(windows)] SeekWrite => write!(formatter, "failed to seek and write to `{}`", path), #[cfg(unix)] ReadAt => write!(formatter, "failed to read with offset from `{}`", path), #[cfg(unix)] WriteAt => write!(formatter, "failed to write with offset to `{}`", path), } } } impl StdError for Error { fn cause(&self) -> Option<&dyn StdError> { self.source() } fn source(&self) -> Option<&(dyn StdError + 'static)> { Some(&self.source) } } #[derive(Debug, Clone, Copy)] pub(crate) enum SourceDestErrorKind { Copy, HardLink, Rename, SoftLink, #[cfg(unix)] Symlink, #[cfg(windows)] SymlinkDir, #[cfg(windows)] SymlinkFile, } /// Error type used by functions like `fs::copy` that holds two paths. #[derive(Debug)] pub(crate) struct SourceDestError { kind: SourceDestErrorKind, source: io::Error, from_path: PathBuf, to_path: PathBuf, } impl SourceDestError { pub fn build( source: io::Error, kind: SourceDestErrorKind, from_path: impl Into, to_path: impl Into, ) -> io::Error { io::Error::new( source.kind(), Self { kind, source, from_path: from_path.into(), to_path: to_path.into(), }, ) } } impl fmt::Display for SourceDestError { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { let from = self.from_path.display(); let to = self.to_path.display(); match self.kind { SourceDestErrorKind::Copy => { write!(formatter, "failed to copy file from {} to {}", from, to) } SourceDestErrorKind::HardLink => { write!(formatter, "failed to hardlink file from {} to {}", from, to) } SourceDestErrorKind::Rename => { write!(formatter, "failed to rename file from {} to {}", from, to) } SourceDestErrorKind::SoftLink => { write!(formatter, "failed to softlink file from {} to {}", from, to) } #[cfg(unix)] SourceDestErrorKind::Symlink => { write!(formatter, "failed to symlink file from {} to {}", from, to) } #[cfg(windows)] SourceDestErrorKind::SymlinkFile => { write!(formatter, "failed to symlink file from {} to {}", from, to) } #[cfg(windows)] SourceDestErrorKind::SymlinkDir => { write!(formatter, "failed to symlink dir from {} to {}", from, to) } } } } impl StdError for SourceDestError { fn cause(&self) -> Option<&dyn StdError> { self.source() } fn source(&self) -> Option<&(dyn StdError + 'static)> { Some(&self.source) } } fs-err-2.11.0/src/file.rs000064400000000000000000000312310072674642500132070ustar 00000000000000use std::fs; use std::io::{self, Read, Seek, Write}; use std::path::{Path, PathBuf}; use crate::errors::{Error, ErrorKind}; /// Wrapper around [`std::fs::File`][std::fs::File] which adds more helpful /// information to all errors. /// /// [std::fs::File]: https://doc.rust-lang.org/stable/std/fs/struct.File.html #[derive(Debug)] pub struct File { file: fs::File, path: PathBuf, } // Opens a std File and returns it or an error generator which only needs the path to produce the error. // Exists for the `crate::read*` functions so they don't unconditionally build a PathBuf. pub(crate) fn open(path: &Path) -> Result io::Error> { fs::File::open(path).map_err(|err| |path| Error::build(err, ErrorKind::OpenFile, path)) } // like `open()` but for `crate::write` pub(crate) fn create(path: &Path) -> Result io::Error> { fs::File::create(path).map_err(|err| |path| Error::build(err, ErrorKind::CreateFile, path)) } /// Wrappers for methods from [`std::fs::File`][std::fs::File]. /// /// [std::fs::File]: https://doc.rust-lang.org/stable/std/fs/struct.File.html impl File { /// Attempts to open a file in read-only mode. /// /// Wrapper for [`File::open`](https://doc.rust-lang.org/stable/std/fs/struct.File.html#method.open). pub fn open

(path: P) -> Result where P: Into, { let path = path.into(); match open(&path) { Ok(file) => Ok(File::from_parts(file, path)), Err(err_gen) => Err(err_gen(path)), } } /// Opens a file in write-only mode. /// /// Wrapper for [`File::create`](https://doc.rust-lang.org/stable/std/fs/struct.File.html#method.create). pub fn create

(path: P) -> Result where P: Into, { let path = path.into(); match create(&path) { Ok(file) => Ok(File::from_parts(file, path)), Err(err_gen) => Err(err_gen(path)), } } /// Wrapper for [`OpenOptions::open`](https://doc.rust-lang.org/stable/std/fs/struct.OpenOptions.html#method.open). /// /// This takes [`&std::fs::OpenOptions`](https://doc.rust-lang.org/stable/std/fs/struct.OpenOptions.html), /// not [`crate::OpenOptions`]. #[deprecated = "use fs_err::OpenOptions::open instead"] pub fn from_options

(path: P, options: &fs::OpenOptions) -> Result where P: Into, { let path = path.into(); match options.open(&path) { Ok(file) => Ok(File::from_parts(file, path)), Err(source) => Err(Error::build(source, ErrorKind::OpenFile, path)), } } /// Attempts to sync all OS-internal metadata to disk. /// /// Wrapper for [`File::sync_all`](https://doc.rust-lang.org/stable/std/fs/struct.File.html#method.sync_all). pub fn sync_all(&self) -> Result<(), io::Error> { self.file .sync_all() .map_err(|source| self.error(source, ErrorKind::SyncFile)) } /// This function is similar to [`sync_all`], except that it might not synchronize file metadata to the filesystem. /// /// Wrapper for [`File::sync_data`](https://doc.rust-lang.org/stable/std/fs/struct.File.html#method.sync_data). pub fn sync_data(&self) -> Result<(), io::Error> { self.file .sync_data() .map_err(|source| self.error(source, ErrorKind::SyncFile)) } /// Truncates or extends the underlying file, updating the size of this file to become `size`. /// /// Wrapper for [`File::set_len`](https://doc.rust-lang.org/stable/std/fs/struct.File.html#method.set_len). pub fn set_len(&self, size: u64) -> Result<(), io::Error> { self.file .set_len(size) .map_err(|source| self.error(source, ErrorKind::SetLen)) } /// Queries metadata about the underlying file. /// /// Wrapper for [`File::metadata`](https://doc.rust-lang.org/stable/std/fs/struct.File.html#method.metadata). pub fn metadata(&self) -> Result { self.file .metadata() .map_err(|source| self.error(source, ErrorKind::Metadata)) } /// Creates a new `File` instance that shares the same underlying file handle as the /// existing `File` instance. Reads, writes, and seeks will affect both `File` /// instances simultaneously. /// /// Wrapper for [`File::try_clone`](https://doc.rust-lang.org/stable/std/fs/struct.File.html#method.try_clone). pub fn try_clone(&self) -> Result { self.file .try_clone() .map(|file| File { file, path: self.path.clone(), }) .map_err(|source| self.error(source, ErrorKind::Clone)) } /// Changes the permissions on the underlying file. /// /// Wrapper for [`File::set_permissions`](https://doc.rust-lang.org/stable/std/fs/struct.File.html#method.set_permissions). pub fn set_permissions(&self, perm: fs::Permissions) -> Result<(), io::Error> { self.file .set_permissions(perm) .map_err(|source| self.error(source, ErrorKind::SetPermissions)) } } /// Methods added by fs-err that are not available on /// [`std::fs::File`][std::fs::File]. /// /// [std::fs::File]: https://doc.rust-lang.org/stable/std/fs/struct.File.html impl File { /// Creates a [`File`](struct.File.html) from a raw file and its path. pub fn from_parts

(file: fs::File, path: P) -> Self where P: Into, { File { file, path: path.into(), } } /// Extract the raw file and its path from this [`File`](struct.File.html) pub fn into_parts(self) -> (fs::File, PathBuf) { (self.file, self.path) } /// Returns a reference to the underlying [`std::fs::File`][std::fs::File]. /// /// [std::fs::File]: https://doc.rust-lang.org/stable/std/fs/struct.File.html pub fn file(&self) -> &fs::File { &self.file } /// Returns a mutable reference to the underlying [`std::fs::File`][std::fs::File]. /// /// [std::fs::File]: https://doc.rust-lang.org/stable/std/fs/struct.File.html pub fn file_mut(&mut self) -> &mut fs::File { &mut self.file } /// Returns a reference to the path that this file was created with. pub fn path(&self) -> &Path { &self.path } /// Wrap the error in information specific to this `File` object. fn error(&self, source: io::Error, kind: ErrorKind) -> io::Error { Error::build(source, kind, &self.path) } } impl Read for File { fn read(&mut self, buf: &mut [u8]) -> std::io::Result { self.file .read(buf) .map_err(|source| self.error(source, ErrorKind::Read)) } fn read_vectored(&mut self, bufs: &mut [std::io::IoSliceMut<'_>]) -> std::io::Result { self.file .read_vectored(bufs) .map_err(|source| self.error(source, ErrorKind::Read)) } } impl<'a> Read for &'a File { fn read(&mut self, buf: &mut [u8]) -> std::io::Result { (&self.file) .read(buf) .map_err(|source| self.error(source, ErrorKind::Read)) } fn read_vectored(&mut self, bufs: &mut [std::io::IoSliceMut<'_>]) -> std::io::Result { (&self.file) .read_vectored(bufs) .map_err(|source| self.error(source, ErrorKind::Read)) } } impl From for fs::File { fn from(file: File) -> Self { file.into_parts().0 } } impl Seek for File { fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result { self.file .seek(pos) .map_err(|source| self.error(source, ErrorKind::Seek)) } } impl<'a> Seek for &'a File { fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result { (&self.file) .seek(pos) .map_err(|source| self.error(source, ErrorKind::Seek)) } } impl Write for File { fn write(&mut self, buf: &[u8]) -> std::io::Result { self.file .write(buf) .map_err(|source| self.error(source, ErrorKind::Write)) } fn write_vectored(&mut self, bufs: &[std::io::IoSlice<'_>]) -> std::io::Result { self.file .write_vectored(bufs) .map_err(|source| self.error(source, ErrorKind::Write)) } fn flush(&mut self) -> std::io::Result<()> { self.file .flush() .map_err(|source| self.error(source, ErrorKind::Flush)) } } impl<'a> Write for &'a File { fn write(&mut self, buf: &[u8]) -> std::io::Result { (&self.file) .write(buf) .map_err(|source| self.error(source, ErrorKind::Write)) } fn write_vectored(&mut self, bufs: &[std::io::IoSlice<'_>]) -> std::io::Result { (&self.file) .write_vectored(bufs) .map_err(|source| self.error(source, ErrorKind::Write)) } fn flush(&mut self) -> std::io::Result<()> { (&self.file) .flush() .map_err(|source| self.error(source, ErrorKind::Flush)) } } #[cfg(unix)] mod unix { use crate::os::unix::fs::FileExt; use crate::ErrorKind; use std::io; use std::os::unix::fs::FileExt as _; use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd}; impl AsRawFd for crate::File { fn as_raw_fd(&self) -> RawFd { self.file().as_raw_fd() } } impl IntoRawFd for crate::File { fn into_raw_fd(self) -> RawFd { self.file.into_raw_fd() } } impl FileExt for crate::File { fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result { self.file() .read_at(buf, offset) .map_err(|err| self.error(err, ErrorKind::ReadAt)) } fn write_at(&self, buf: &[u8], offset: u64) -> io::Result { self.file() .write_at(buf, offset) .map_err(|err| self.error(err, ErrorKind::WriteAt)) } } #[cfg(feature = "io_safety")] mod io_safety { use std::os::unix::io::{AsFd, BorrowedFd, OwnedFd}; #[cfg_attr(docsrs, doc(cfg(feature = "io_safety")))] impl AsFd for crate::File { fn as_fd(&self) -> BorrowedFd<'_> { self.file().as_fd() } } #[cfg_attr(docsrs, doc(cfg(feature = "io_safety")))] impl From for OwnedFd { fn from(file: crate::File) -> Self { file.into_parts().0.into() } } } } #[cfg(windows)] mod windows { use crate::os::windows::fs::FileExt; use crate::ErrorKind; use std::io; use std::os::windows::{ fs::FileExt as _, io::{AsRawHandle, IntoRawHandle, RawHandle}, }; impl FileExt for crate::File { fn seek_read(&self, buf: &mut [u8], offset: u64) -> io::Result { self.file() .seek_read(buf, offset) .map_err(|err| self.error(err, ErrorKind::SeekRead)) } fn seek_write(&self, buf: &[u8], offset: u64) -> io::Result { self.file() .seek_write(buf, offset) .map_err(|err| self.error(err, ErrorKind::SeekWrite)) } } impl AsRawHandle for crate::File { fn as_raw_handle(&self) -> RawHandle { self.file().as_raw_handle() } } // can't be implemented, because the trait doesn't give us a Path // impl std::os::windows::io::FromRawHandle for crate::File { // } impl IntoRawHandle for crate::File { fn into_raw_handle(self) -> RawHandle { self.file.into_raw_handle() } } #[cfg(feature = "io_safety")] mod io_safety { use std::os::windows::io::{AsHandle, BorrowedHandle, OwnedHandle}; #[cfg_attr(docsrs, doc(cfg(feature = "io_safety")))] impl AsHandle for crate::File { fn as_handle(&self) -> BorrowedHandle<'_> { self.file().as_handle() } } #[cfg_attr(docsrs, doc(cfg(feature = "io_safety")))] impl From for OwnedHandle { fn from(file: crate::File) -> Self { file.into_parts().0.into() } } } } fs-err-2.11.0/src/lib.rs000064400000000000000000000236710072674642500130470ustar 00000000000000/*! fs-err is a drop-in replacement for [`std::fs`][std::fs] that provides more helpful messages on errors. Extra information includes which operations was attempted and any involved paths. # Error Messages Using [`std::fs`][std::fs], if this code fails: ```no_run # use std::fs::File; let file = File::open("does not exist.txt")?; # Ok::<(), std::io::Error>(()) ``` The error message that Rust gives you isn't very useful: ```txt The system cannot find the file specified. (os error 2) ``` ...but if we use fs-err instead, our error contains more actionable information: ```txt failed to open file `does not exist.txt` caused by: The system cannot find the file specified. (os error 2) ``` # Usage fs-err's API is the same as [`std::fs`][std::fs], so migrating code to use it is easy. ```no_run // use std::fs; use fs_err as fs; let contents = fs::read_to_string("foo.txt")?; println!("Read foo.txt: {}", contents); # Ok::<(), std::io::Error>(()) ``` fs-err uses [`std::io::Error`][std::io::Error] for all errors. This helps fs-err compose well with traits from the standard library like [`std::io::Read`][std::io::Read] and crates that use them like [`serde_json`][serde_json]: ```no_run use fs_err::File; let file = File::open("my-config.json")?; // If an I/O error occurs inside serde_json, the error will include a file path // as well as what operation was being performed. let decoded: Vec = serde_json::from_reader(file)?; println!("Program config: {:?}", decoded); # Ok::<(), Box>(()) ``` [std::fs]: https://doc.rust-lang.org/stable/std/fs/ [std::io::Error]: https://doc.rust-lang.org/stable/std/io/struct.Error.html [std::io::Read]: https://doc.rust-lang.org/stable/std/io/trait.Read.html [serde_json]: https://crates.io/crates/serde_json */ #![doc(html_root_url = "https://docs.rs/fs-err/2.11.0")] #![deny(missing_debug_implementations, missing_docs)] #![cfg_attr(docsrs, feature(doc_cfg))] mod dir; mod errors; mod file; mod open_options; pub mod os; mod path; #[cfg(feature = "tokio")] #[cfg_attr(docsrs, doc(cfg(feature = "tokio")))] pub mod tokio; use std::fs; use std::io::{self, Read, Write}; use std::path::{Path, PathBuf}; use errors::{Error, ErrorKind, SourceDestError, SourceDestErrorKind}; pub use dir::*; pub use file::*; pub use open_options::OpenOptions; pub use path::PathExt; /// Read the entire contents of a file into a bytes vector. /// /// Wrapper for [`fs::read`](https://doc.rust-lang.org/stable/std/fs/fn.read.html). pub fn read>(path: P) -> io::Result> { let path = path.as_ref(); let mut file = file::open(path).map_err(|err_gen| err_gen(path.to_path_buf()))?; let mut bytes = Vec::with_capacity(initial_buffer_size(&file)); file.read_to_end(&mut bytes) .map_err(|err| Error::build(err, ErrorKind::Read, path))?; Ok(bytes) } /// Read the entire contents of a file into a string. /// /// Wrapper for [`fs::read_to_string`](https://doc.rust-lang.org/stable/std/fs/fn.read_to_string.html). pub fn read_to_string>(path: P) -> io::Result { let path = path.as_ref(); let mut file = file::open(path).map_err(|err_gen| err_gen(path.to_path_buf()))?; let mut string = String::with_capacity(initial_buffer_size(&file)); file.read_to_string(&mut string) .map_err(|err| Error::build(err, ErrorKind::Read, path))?; Ok(string) } /// Write a slice as the entire contents of a file. /// /// Wrapper for [`fs::write`](https://doc.rust-lang.org/stable/std/fs/fn.write.html). pub fn write, C: AsRef<[u8]>>(path: P, contents: C) -> io::Result<()> { let path = path.as_ref(); file::create(path) .map_err(|err_gen| err_gen(path.to_path_buf()))? .write_all(contents.as_ref()) .map_err(|err| Error::build(err, ErrorKind::Write, path)) } /// Copies the contents of one file to another. This function will also copy the /// permission bits of the original file to the destination file. /// /// Wrapper for [`fs::copy`](https://doc.rust-lang.org/stable/std/fs/fn.copy.html). pub fn copy(from: P, to: Q) -> io::Result where P: AsRef, Q: AsRef, { let from = from.as_ref(); let to = to.as_ref(); fs::copy(from, to) .map_err(|source| SourceDestError::build(source, SourceDestErrorKind::Copy, from, to)) } /// Creates a new, empty directory at the provided path. /// /// Wrapper for [`fs::create_dir`](https://doc.rust-lang.org/stable/std/fs/fn.create_dir.html). pub fn create_dir

(path: P) -> io::Result<()> where P: AsRef, { let path = path.as_ref(); fs::create_dir(path).map_err(|source| Error::build(source, ErrorKind::CreateDir, path)) } /// Recursively create a directory and all of its parent components if they are missing. /// /// Wrapper for [`fs::create_dir_all`](https://doc.rust-lang.org/stable/std/fs/fn.create_dir_all.html). pub fn create_dir_all

(path: P) -> io::Result<()> where P: AsRef, { let path = path.as_ref(); fs::create_dir_all(path).map_err(|source| Error::build(source, ErrorKind::CreateDir, path)) } /// Removes an empty directory. /// /// Wrapper for [`fs::remove_dir`](https://doc.rust-lang.org/stable/std/fs/fn.remove_dir.html). pub fn remove_dir

(path: P) -> io::Result<()> where P: AsRef, { let path = path.as_ref(); fs::remove_dir(path).map_err(|source| Error::build(source, ErrorKind::RemoveDir, path)) } /// Removes a directory at this path, after removing all its contents. Use carefully! /// /// Wrapper for [`fs::remove_dir_all`](https://doc.rust-lang.org/stable/std/fs/fn.remove_dir_all.html). pub fn remove_dir_all

(path: P) -> io::Result<()> where P: AsRef, { let path = path.as_ref(); fs::remove_dir_all(path).map_err(|source| Error::build(source, ErrorKind::RemoveDir, path)) } /// Removes a file from the filesystem. /// /// Wrapper for [`fs::remove_file`](https://doc.rust-lang.org/stable/std/fs/fn.remove_file.html). pub fn remove_file

(path: P) -> io::Result<()> where P: AsRef, { let path = path.as_ref(); fs::remove_file(path).map_err(|source| Error::build(source, ErrorKind::RemoveFile, path)) } /// Given a path, query the file system to get information about a file, directory, etc. /// /// Wrapper for [`fs::metadata`](https://doc.rust-lang.org/stable/std/fs/fn.metadata.html). pub fn metadata>(path: P) -> io::Result { let path = path.as_ref(); fs::metadata(path).map_err(|source| Error::build(source, ErrorKind::Metadata, path)) } /// Returns the canonical, absolute form of a path with all intermediate components /// normalized and symbolic links resolved. /// /// Wrapper for [`fs::canonicalize`](https://doc.rust-lang.org/stable/std/fs/fn.canonicalize.html). pub fn canonicalize>(path: P) -> io::Result { let path = path.as_ref(); fs::canonicalize(path).map_err(|source| Error::build(source, ErrorKind::Canonicalize, path)) } /// Creates a new hard link on the filesystem. /// /// Wrapper for [`fs::hard_link`](https://doc.rust-lang.org/stable/std/fs/fn.hard_link.html). pub fn hard_link, Q: AsRef>(src: P, dst: Q) -> io::Result<()> { let src = src.as_ref(); let dst = dst.as_ref(); fs::hard_link(src, dst) .map_err(|source| SourceDestError::build(source, SourceDestErrorKind::HardLink, src, dst)) } /// Reads a symbolic link, returning the file that the link points to. /// /// Wrapper for [`fs::read_link`](https://doc.rust-lang.org/stable/std/fs/fn.read_link.html). pub fn read_link>(path: P) -> io::Result { let path = path.as_ref(); fs::read_link(path).map_err(|source| Error::build(source, ErrorKind::ReadLink, path)) } /// Rename a file or directory to a new name, replacing the original file if to already exists. /// /// Wrapper for [`fs::rename`](https://doc.rust-lang.org/stable/std/fs/fn.rename.html). pub fn rename, Q: AsRef>(from: P, to: Q) -> io::Result<()> { let from = from.as_ref(); let to = to.as_ref(); fs::rename(from, to) .map_err(|source| SourceDestError::build(source, SourceDestErrorKind::Rename, from, to)) } /// Wrapper for [`fs::soft_link`](https://doc.rust-lang.org/stable/std/fs/fn.soft_link.html). #[deprecated = "replaced with std::os::unix::fs::symlink and \ std::os::windows::fs::{symlink_file, symlink_dir}"] pub fn soft_link, Q: AsRef>(src: P, dst: Q) -> io::Result<()> { let src = src.as_ref(); let dst = dst.as_ref(); #[allow(deprecated)] fs::soft_link(src, dst) .map_err(|source| SourceDestError::build(source, SourceDestErrorKind::SoftLink, src, dst)) } /// Query the metadata about a file without following symlinks. /// /// Wrapper for [`fs::symlink_metadata`](https://doc.rust-lang.org/stable/std/fs/fn.symlink_metadata.html). pub fn symlink_metadata>(path: P) -> io::Result { let path = path.as_ref(); fs::symlink_metadata(path) .map_err(|source| Error::build(source, ErrorKind::SymlinkMetadata, path)) } /// Changes the permissions found on a file or a directory. /// /// Wrapper for [`fs::set_permissions`](https://doc.rust-lang.org/stable/std/fs/fn.set_permissions.html). pub fn set_permissions>(path: P, perm: fs::Permissions) -> io::Result<()> { let path = path.as_ref(); fs::set_permissions(path, perm) .map_err(|source| Error::build(source, ErrorKind::SetPermissions, path)) } fn initial_buffer_size(file: &std::fs::File) -> usize { file.metadata().map(|m| m.len() as usize + 1).unwrap_or(0) } pub(crate) use private::Sealed; mod private { pub trait Sealed {} impl Sealed for crate::File {} impl Sealed for std::path::Path {} impl Sealed for crate::OpenOptions {} } fs-err-2.11.0/src/open_options.rs000064400000000000000000000130370072674642500150100ustar 00000000000000use std::{fs, io, path::PathBuf}; #[derive(Clone, Debug)] /// Wrapper around [`std::fs::OpenOptions`](https://doc.rust-lang.org/std/fs/struct.OpenOptions.html) pub struct OpenOptions(fs::OpenOptions); impl OpenOptions { /// Creates a blank new set of options ready for configuration. /// /// Wrapper for [`std::fs::OpenOptions::new`](https://doc.rust-lang.org/std/fs/struct.OpenOptions.html#method.new) #[allow(clippy::new_without_default)] pub fn new() -> Self { OpenOptions(fs::OpenOptions::new()) } /// Sets the option for read access. /// /// Wrapper for [`std::fs::OpenOptions::read`](https://doc.rust-lang.org/std/fs/struct.OpenOptions.html#method.read) pub fn read(&mut self, read: bool) -> &mut Self { self.0.read(read); self } /// Sets the option for write access. /// /// Wrapper for [`std::fs::OpenOptions::write`](https://doc.rust-lang.org/std/fs/struct.OpenOptions.html#method.write) pub fn write(&mut self, write: bool) -> &mut Self { self.0.write(write); self } /// Sets the option for the append mode. /// /// Wrapper for [`std::fs::OpenOptions::append`](https://doc.rust-lang.org/std/fs/struct.OpenOptions.html#method.append) pub fn append(&mut self, append: bool) -> &mut Self { self.0.append(append); self } /// Sets the option for truncating a previous file. /// /// Wrapper for [`std::fs::OpenOptions::truncate`](https://doc.rust-lang.org/std/fs/struct.OpenOptions.html#method.truncate) pub fn truncate(&mut self, truncate: bool) -> &mut Self { self.0.truncate(truncate); self } /// Sets the option to create a new file, or open it if it already exists. /// /// Wrapper for [`std::fs::OpenOptions::create`](https://doc.rust-lang.org/std/fs/struct.OpenOptions.html#method.create) pub fn create(&mut self, create: bool) -> &mut Self { self.0.create(create); self } /// Sets the option to create a new file, failing if it already exists. /// /// Wrapper for [`std::fs::OpenOptions::create_new`](https://doc.rust-lang.org/std/fs/struct.OpenOptions.html#method.create_new) pub fn create_new(&mut self, create_new: bool) -> &mut Self { self.0.create_new(create_new); self } /// Opens a file at `path` with the options specified by `self`. /// /// Wrapper for [`std::fs::OpenOptions::open`](https://doc.rust-lang.org/std/fs/struct.OpenOptions.html#method.open) pub fn open

(&self, path: P) -> io::Result where P: Into, { // We have to either duplicate the logic or call the deprecated method here. // We can't let the deprecated function call this method, because we can't construct // `&fs_err::OpenOptions` from `&fs::OpenOptions` without cloning // (although cloning would probably be cheap). #[allow(deprecated)] crate::File::from_options(path.into(), self.options()) } } /// Methods added by fs-err that are not available on /// [`std::fs::OpenOptions`](https://doc.rust-lang.org/stable/std/fs/struct.OpenOptions.html). impl OpenOptions { /// Constructs `Self` from [`std::fs::OpenOptions`](https://doc.rust-lang.org/stable/std/fs/struct.OpenOptions.html) pub fn from_options(options: fs::OpenOptions) -> Self { Self(options) } /// Returns a reference to the underlying [`std::fs::OpenOptions`](https://doc.rust-lang.org/stable/std/fs/struct.OpenOptions.html). /// /// Note that calling `open()` on this reference will NOT give you the improved errors from fs-err. pub fn options(&self) -> &fs::OpenOptions { &self.0 } /// Returns a mutable reference to the underlying [`std::fs::OpenOptions`](https://doc.rust-lang.org/stable/std/fs/struct.OpenOptions.html). /// /// This allows you to change settings that don't yet have wrappers in fs-err. /// Note that calling `open()` on this reference will NOT give you the improved errors from fs-err. pub fn options_mut(&mut self) -> &mut fs::OpenOptions { &mut self.0 } } #[cfg(unix)] mod unix { use crate::os::unix::fs::OpenOptionsExt; use std::os::unix::fs::OpenOptionsExt as _; impl OpenOptionsExt for crate::OpenOptions { fn mode(&mut self, mode: u32) -> &mut Self { self.options_mut().mode(mode); self } fn custom_flags(&mut self, flags: i32) -> &mut Self { self.options_mut().custom_flags(flags); self } } } #[cfg(windows)] mod windows { use crate::os::windows::fs::OpenOptionsExt; use std::os::windows::fs::OpenOptionsExt as _; impl OpenOptionsExt for crate::OpenOptions { fn access_mode(&mut self, access: u32) -> &mut Self { self.options_mut().access_mode(access); self } fn share_mode(&mut self, val: u32) -> &mut Self { self.options_mut().share_mode(val); self } fn custom_flags(&mut self, flags: u32) -> &mut Self { self.options_mut().custom_flags(flags); self } fn attributes(&mut self, val: u32) -> &mut Self { self.options_mut().attributes(val); self } fn security_qos_flags(&mut self, flags: u32) -> &mut Self { self.options_mut().security_qos_flags(flags); self } } } fs-err-2.11.0/src/os/unix.rs000064400000000000000000000043710072674642500137010ustar 00000000000000/// Unix-specific extensions to wrappers in `fs_err` for `std::fs` types. pub mod fs { use std::io; use std::path::Path; use crate::SourceDestError; use crate::SourceDestErrorKind; /// Creates a new symbolic link on the filesystem. /// /// Wrapper for [`std::os::unix::fs::symlink`](https://doc.rust-lang.org/std/os/unix/fs/fn.symlink.html) pub fn symlink, Q: AsRef>(src: P, dst: Q) -> io::Result<()> { let src = src.as_ref(); let dst = dst.as_ref(); std::os::unix::fs::symlink(src, dst) .map_err(|err| SourceDestError::build(err, SourceDestErrorKind::Symlink, src, dst)) } /// Wrapper for [`std::os::unix::fs::FileExt`](https://doc.rust-lang.org/std/os/unix/fs/trait.FileExt.html). /// /// The std traits might be extended in the future (See issue [#49961](https://github.com/rust-lang/rust/issues/49961#issuecomment-382751777)). /// This trait is sealed and can not be implemented by other crates. pub trait FileExt: crate::Sealed { /// Wrapper for [`FileExt::read_at`](https://doc.rust-lang.org/std/os/unix/fs/trait.FileExt.html#tymethod.read_at) fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result; /// Wrapper for [`FileExt::write_at`](https://doc.rust-lang.org/std/os/unix/fs/trait.FileExt.html#tymethod.write_at) fn write_at(&self, buf: &[u8], offset: u64) -> io::Result; } /// Wrapper for [`std::os::unix::fs::OpenOptionsExt`](https://doc.rust-lang.org/std/os/unix/fs/trait.OpenOptionsExt.html) /// /// The std traits might be extended in the future (See issue [#49961](https://github.com/rust-lang/rust/issues/49961#issuecomment-382751777)). /// This trait is sealed and can not be implemented by other crates. pub trait OpenOptionsExt: crate::Sealed { /// Wrapper for [`OpenOptionsExt::mode`](https://doc.rust-lang.org/std/os/unix/fs/trait.OpenOptionsExt.html#tymethod.mode) fn mode(&mut self, mode: u32) -> &mut Self; /// Wrapper for [`OpenOptionsExt::custom_flags`](https://doc.rust-lang.org/std/os/unix/fs/trait.OpenOptionsExt.html#tymethod.custom_flags) fn custom_flags(&mut self, flags: i32) -> &mut Self; } } fs-err-2.11.0/src/os/windows.rs000064400000000000000000000067360072674642500144170ustar 00000000000000/// Windows-specific extensions to wrappers in `fs_err` for `std::fs` types. pub mod fs { use crate::{SourceDestError, SourceDestErrorKind}; use std::io; use std::path::Path; /// Creates a new symlink to a directory on the filesystem. /// /// Wrapper for [std::os::windows::fs::symlink_dir](https://doc.rust-lang.org/std/os/windows/fs/fn.symlink_dir.html) pub fn symlink_dir, Q: AsRef>(src: P, dst: Q) -> io::Result<()> { let src = src.as_ref(); let dst = dst.as_ref(); std::os::windows::fs::symlink_dir(src, dst) .map_err(|err| SourceDestError::build(err, SourceDestErrorKind::SymlinkDir, src, dst)) } /// Creates a new symlink to a non-directory file on the filesystem. /// /// Wrapper for [std::os::windows::fs::symlink_file](https://doc.rust-lang.org/std/os/windows/fs/fn.symlink_file.html) pub fn symlink_file, Q: AsRef>(src: P, dst: Q) -> io::Result<()> { let src = src.as_ref(); let dst = dst.as_ref(); std::os::windows::fs::symlink_file(src, dst) .map_err(|err| SourceDestError::build(err, SourceDestErrorKind::SymlinkFile, src, dst)) } /// Wrapper for [`std::os::windows::fs::FileExt`](https://doc.rust-lang.org/std/os/windows/fs/trait.FileExt.html). /// /// The std traits might be extended in the future (See issue [#49961](https://github.com/rust-lang/rust/issues/49961#issuecomment-382751777)). /// This trait is sealed and can not be implemented by other crates. pub trait FileExt: crate::Sealed { /// Wrapper for [`FileExt::seek_read`](https://doc.rust-lang.org/std/os/windows/fs/trait.FileExt.html#tymethod.seek_read) fn seek_read(&self, buf: &mut [u8], offset: u64) -> io::Result; /// Wrapper for [`FileExt::seek_wriite`](https://doc.rust-lang.org/std/os/windows/fs/trait.FileExt.html#tymethod.seek_write) fn seek_write(&self, buf: &[u8], offset: u64) -> io::Result; } /// Wrapper for [`std::os::windows::fs::OpenOptionsExt`](https://doc.rust-lang.org/std/os/windows/fs/trait.OpenOptionsExt.html) /// /// The std traits might be extended in the future (See issue [#49961](https://github.com/rust-lang/rust/issues/49961#issuecomment-382751777)). /// This trait is sealed and can not be implemented by other crates. pub trait OpenOptionsExt: crate::Sealed { /// Wrapper for [`OpenOptionsExt::access_mode`](https://doc.rust-lang.org/std/os/windows/fs/trait.OpenOptionsExt.html#tymethod.access_mode) fn access_mode(&mut self, access: u32) -> &mut Self; /// Wrapper for [`OpenOptionsExt::share_mode`](https://doc.rust-lang.org/std/os/windows/fs/trait.OpenOptionsExt.html#tymethod.share_mode) fn share_mode(&mut self, val: u32) -> &mut Self; /// Wrapper for [`OpenOptionsExt::custom_flags`](https://doc.rust-lang.org/std/os/windows/fs/trait.OpenOptionsExt.html#tymethod.custom_flags) fn custom_flags(&mut self, flags: u32) -> &mut Self; /// Wrapper for [`OpenOptionsExt::attributes`](https://doc.rust-lang.org/std/os/windows/fs/trait.OpenOptionsExt.html#tymethod.attributes) fn attributes(&mut self, val: u32) -> &mut Self; /// Wrapper for [`OpenOptionsExt::security_qos_flags`](https://doc.rust-lang.org/std/os/windows/fs/trait.OpenOptionsExt.html#tymethod.security_qos_flags) fn security_qos_flags(&mut self, flags: u32) -> &mut Self; } } fs-err-2.11.0/src/os.rs000064400000000000000000000005560072674642500127170ustar 00000000000000//! OS-specific functionality. // The std-library has a couple more platforms than just `unix` for which these apis // are defined, but we're using just `unix` here. We can always expand later. #[cfg(unix)] /// Platform-specific extensions for Unix platforms. pub mod unix; #[cfg(windows)] /// Platform-specific extensions for Windows. pub mod windows; fs-err-2.11.0/src/path.rs000064400000000000000000000046510072674642500132320ustar 00000000000000#[allow(unused_imports)] use crate::errors::{Error, ErrorKind}; use std::fs; use std::io; use std::path::{Path, PathBuf}; /// Defines aliases on [`Path`](https://doc.rust-lang.org/std/path/struct.Path.html) for `fs_err` functions. /// /// This trait is sealed and can not be implemented by other crates. // // Because no one else can implement it, we can add methods backwards-compatibly. pub trait PathExt: crate::Sealed { /// Returns Ok(true) if the path points at an existing entity. /// /// Wrapper for [`Path::try_exists`](https://doc.rust-lang.org/std/path/struct.Path.html#method.try_exists). #[cfg(rustc_1_63)] fn fs_err_try_exists(&self) -> io::Result; /// Given a path, query the file system to get information about a file, directory, etc. /// /// Wrapper for [`crate::metadata`]. fn fs_err_metadata(&self) -> io::Result; /// Query the metadata about a file without following symlinks. /// /// Wrapper for [`crate::symlink_metadata`]. fn fs_err_symlink_metadata(&self) -> io::Result; /// Returns the canonical, absolute form of a path with all intermediate components /// normalized and symbolic links resolved. /// /// Wrapper for [`crate::canonicalize`]. fn fs_err_canonicalize(&self) -> io::Result; /// Reads a symbolic link, returning the file that the link points to. /// /// Wrapper for [`crate::read_link`]. fn fs_err_read_link(&self) -> io::Result; /// Returns an iterator over the entries within a directory. /// /// Wrapper for [`crate::read_dir`]. fn fs_err_read_dir(&self) -> io::Result; } impl PathExt for Path { #[cfg(rustc_1_63)] fn fs_err_try_exists(&self) -> io::Result { self.try_exists() .map_err(|source| Error::build(source, ErrorKind::FileExists, self)) } fn fs_err_metadata(&self) -> io::Result { crate::metadata(self) } fn fs_err_symlink_metadata(&self) -> io::Result { crate::symlink_metadata(self) } fn fs_err_canonicalize(&self) -> io::Result { crate::canonicalize(self) } fn fs_err_read_link(&self) -> io::Result { crate::read_link(self) } fn fs_err_read_dir(&self) -> io::Result { crate::read_dir(self) } } fs-err-2.11.0/src/tokio/dir_builder.rs000064400000000000000000000035040072674642500157030ustar 00000000000000use crate::errors::{Error, ErrorKind}; use std::io; use std::path::Path; /// A builder for creating directories in various manners. /// /// This is a wrapper around [`tokio::fs::DirBuilder`]. #[derive(Debug, Default)] #[cfg_attr(docsrs, doc(cfg(feature = "tokio")))] pub struct DirBuilder { inner: tokio::fs::DirBuilder, } impl DirBuilder { /// Creates a new set of options with default mode/security settings for all /// platforms and also non-recursive. /// /// This is a wrapper version of [`tokio::fs::DirBuilder::new`] /// /// # Examples /// /// ```no_run /// use fs_err::tokio::DirBuilder; /// /// let builder = DirBuilder::new(); /// ``` pub fn new() -> Self { Default::default() } /// Indicates whether to create directories recursively (including all parent /// directories). Parents that do not exist are created with the same security and /// permissions settings. /// /// Wrapper around [`tokio::fs::DirBuilder::recursive`]. pub fn recursive(&mut self, recursive: bool) -> &mut Self { self.inner.recursive(recursive); self } /// Creates the specified directory with the configured options. /// /// Wrapper around [`tokio::fs::DirBuilder::create`]. pub async fn create(&self, path: impl AsRef) -> io::Result<()> { let path = path.as_ref(); self.inner .create(path) .await .map_err(|err| Error::build(err, ErrorKind::CreateDir, path)) } } #[cfg(unix)] impl DirBuilder { /// Sets the mode to create new directories with. /// /// Wrapper around [`tokio::fs::DirBuilder::mode`]. pub fn mode(&mut self, mode: u32) -> &mut Self { self.inner.mode(mode); self } } fs-err-2.11.0/src/tokio/file.rs000064400000000000000000000207230072674642500143400ustar 00000000000000use crate::errors::{Error, ErrorKind}; use std::fs::{Metadata, Permissions}; use std::io; use std::io::{IoSlice, SeekFrom}; use std::path::{Path, PathBuf}; use std::pin::Pin; use std::task::{ready, Context, Poll}; use tokio::fs; use tokio::fs::File as TokioFile; use tokio::io::{AsyncRead, AsyncSeek, AsyncWrite, ReadBuf}; /// Wrapper around [`tokio::fs::File`] which adds more helpful /// information to all errors. #[derive(Debug)] #[cfg_attr(docsrs, doc(cfg(feature = "tokio")))] pub struct File { tokio: fs::File, path: PathBuf, } impl File { /// Attempts to open a file in read-only mode. /// /// Wrapper for [`tokio::fs::File::open`]. pub async fn open(path: impl Into) -> io::Result { let path = path.into(); let f = TokioFile::open(&path) .await .map_err(|err| Error::build(err, ErrorKind::OpenFile, &path))?; Ok(File::from_parts(f, path)) } /// Opens a file in write-only mode. /// /// Wrapper for [`tokio::fs::File::create`]. pub async fn create(path: impl Into) -> io::Result { let path = path.into(); match TokioFile::create(&path).await { Ok(f) => Ok(File::from_parts(f, path)), Err(err) => Err(Error::build(err, ErrorKind::CreateFile, &path)), } } /// Converts a [`crate::File`] to a [`tokio::fs::File`]. /// /// Wrapper for [`tokio::fs::File::from_std`]. pub fn from_std(std: crate::File) -> File { let (std, path) = std.into_parts(); File::from_parts(TokioFile::from_std(std), path) } /// Attempts to sync all OS-internal metadata to disk. /// /// Wrapper for [`tokio::fs::File::sync_all`]. pub async fn sync_all(&self) -> io::Result<()> { self.tokio .sync_all() .await .map_err(|err| self.error(err, ErrorKind::SyncFile)) } /// This function is similar to `sync_all`, except that it may not /// synchronize file metadata to the filesystem. /// /// Wrapper for [`tokio::fs::File::sync_data`]. pub async fn sync_data(&self) -> io::Result<()> { self.tokio .sync_data() .await .map_err(|err| self.error(err, ErrorKind::SyncFile)) } /// Truncates or extends the underlying file, updating the size of this file to become size. /// /// Wrapper for [`tokio::fs::File::set_len`]. pub async fn set_len(&self, size: u64) -> io::Result<()> { self.tokio .set_len(size) .await .map_err(|err| self.error(err, ErrorKind::SetLen)) } /// Queries metadata about the underlying file. /// /// Wrapper for [`tokio::fs::File::metadata`]. pub async fn metadata(&self) -> io::Result { self.tokio .metadata() .await .map_err(|err| self.error(err, ErrorKind::Metadata)) } /// Creates a new `File` instance that shares the same underlying file handle /// as the existing `File` instance. Reads, writes, and seeks will affect both /// `File` instances simultaneously. /// /// Wrapper for [`tokio::fs::File::try_clone`]. pub async fn try_clone(&self) -> io::Result { match self.tokio.try_clone().await { Ok(file) => Ok(File::from_parts(file, self.path.clone())), Err(err) => Err(self.error(err, ErrorKind::Clone)), } } /// Destructures `File` into a [`crate::File`]. This function is async to allow any /// in-flight operations to complete. /// /// Wrapper for [`tokio::fs::File::into_std`]. pub async fn into_std(self) -> crate::File { crate::File::from_parts(self.tokio.into_std().await, self.path) } /// Tries to immediately destructure `File` into a [`crate::File`]. /// /// Wrapper for [`tokio::fs::File::try_into_std`]. pub fn try_into_std(self) -> Result { match self.tokio.try_into_std() { Ok(f) => Ok(crate::File::from_parts(f, self.path)), Err(f) => Err(File::from_parts(f, self.path)), } } /// Changes the permissions on the underlying file. /// /// Wrapper for [`tokio::fs::File::set_permissions`]. pub async fn set_permissions(&self, perm: Permissions) -> io::Result<()> { self.tokio .set_permissions(perm) .await .map_err(|err| self.error(err, ErrorKind::SetPermissions)) } } /// Methods added by fs-err that are not available on /// [`tokio::fs::File`]. impl File { /// Creates a [`File`](struct.File.html) from a raw file and its path. pub fn from_parts

(file: TokioFile, path: P) -> Self where P: Into, { File { tokio: file, path: path.into(), } } /// Extract the raw file and its path from this [`File`](struct.File.html). pub fn into_parts(self) -> (TokioFile, PathBuf) { (self.tokio, self.path) } /// Returns a reference to the underlying [`tokio::fs::File`]. pub fn file(&self) -> &TokioFile { &self.tokio } /// Returns a mutable reference to the underlying [`tokio::fs::File`]. pub fn file_mut(&mut self) -> &mut TokioFile { &mut self.tokio } /// Returns a reference to the path that this file was created with. pub fn path(&self) -> &Path { &self.path } /// Wrap the error in information specific to this `File` object. fn error(&self, source: io::Error, kind: ErrorKind) -> io::Error { Error::build(source, kind, &self.path) } } impl From for File { fn from(f: crate::File) -> Self { let (f, path) = f.into_parts(); File::from_parts(f.into(), path) } } impl From for TokioFile { fn from(f: File) -> Self { f.into_parts().0 } } #[cfg(unix)] impl std::os::unix::io::AsRawFd for File { fn as_raw_fd(&self) -> std::os::unix::io::RawFd { self.tokio.as_raw_fd() } } #[cfg(windows)] impl std::os::windows::io::AsRawHandle for File { fn as_raw_handle(&self) -> std::os::windows::io::RawHandle { self.tokio.as_raw_handle() } } impl AsyncRead for File { fn poll_read( mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut ReadBuf<'_>, ) -> Poll> { Poll::Ready( ready!(Pin::new(&mut self.tokio).poll_read(cx, buf)) .map_err(|err| self.error(err, ErrorKind::Read)), ) } } impl AsyncSeek for File { fn start_seek(mut self: Pin<&mut Self>, position: SeekFrom) -> io::Result<()> { Pin::new(&mut self.tokio) .start_seek(position) .map_err(|err| self.error(err, ErrorKind::Seek)) } fn poll_complete(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { Poll::Ready( ready!(Pin::new(&mut self.tokio).poll_complete(cx)) .map_err(|err| self.error(err, ErrorKind::Seek)), ) } } impl AsyncWrite for File { fn poll_write( mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8], ) -> Poll> { Poll::Ready( ready!(Pin::new(&mut self.tokio).poll_write(cx, buf)) .map_err(|err| self.error(err, ErrorKind::Write)), ) } fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { Poll::Ready( ready!(Pin::new(&mut self.tokio).poll_flush(cx)) .map_err(|err| self.error(err, ErrorKind::Flush)), ) } fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { Poll::Ready( ready!(Pin::new(&mut self.tokio).poll_shutdown(cx)) .map_err(|err| self.error(err, ErrorKind::Flush)), ) } fn poll_write_vectored( mut self: Pin<&mut Self>, cx: &mut Context<'_>, bufs: &[IoSlice<'_>], ) -> Poll> { Poll::Ready( ready!(Pin::new(&mut self.tokio).poll_write_vectored(cx, bufs)) .map_err(|err| self.error(err, ErrorKind::Write)), ) } fn is_write_vectored(&self) -> bool { self.tokio.is_write_vectored() } } fs-err-2.11.0/src/tokio/mod.rs000064400000000000000000000216440072674642500142030ustar 00000000000000//! Tokio-specific wrappers that use `fs_err` error messages. use crate::errors::{Error, ErrorKind, SourceDestError, SourceDestErrorKind}; use std::fs::{Metadata, Permissions}; use std::path::{Path, PathBuf}; use tokio::io; mod dir_builder; mod file; mod open_options; mod read_dir; pub use self::open_options::OpenOptions; pub use self::read_dir::{read_dir, DirEntry, ReadDir}; pub use dir_builder::DirBuilder; pub use file::File; /// Returns the canonical, absolute form of a path with all intermediate /// components normalized and symbolic links resolved. /// /// Wrapper for [`tokio::fs::canonicalize`]. #[cfg_attr(docsrs, doc(cfg(feature = "tokio")))] pub async fn canonicalize(path: impl AsRef) -> io::Result { let path = path.as_ref(); tokio::fs::canonicalize(path) .await .map_err(|err| Error::build(err, ErrorKind::Canonicalize, path)) } /// Copies the contents of one file to another. This function will also copy the permission bits /// of the original file to the destination file. /// This function will overwrite the contents of to. /// /// Wrapper for [`tokio::fs::copy`]. #[cfg_attr(docsrs, doc(cfg(feature = "tokio")))] pub async fn copy(from: impl AsRef, to: impl AsRef) -> Result { let (from, to) = (from.as_ref(), to.as_ref()); tokio::fs::copy(from, to) .await .map_err(|err| SourceDestError::build(err, SourceDestErrorKind::Copy, from, to)) } /// Creates a new, empty directory at the provided path. /// /// Wrapper for [`tokio::fs::create_dir`]. #[cfg_attr(docsrs, doc(cfg(feature = "tokio")))] pub async fn create_dir(path: impl AsRef) -> io::Result<()> { let path = path.as_ref(); tokio::fs::create_dir(path) .await .map_err(|err| Error::build(err, ErrorKind::CreateDir, path)) } /// Recursively creates a directory and all of its parent components if they /// are missing. /// /// Wrapper for [`tokio::fs::create_dir_all`]. #[cfg_attr(docsrs, doc(cfg(feature = "tokio")))] pub async fn create_dir_all(path: impl AsRef) -> io::Result<()> { let path = path.as_ref(); tokio::fs::create_dir_all(path) .await .map_err(|err| Error::build(err, ErrorKind::CreateDir, path)) } /// Creates a new hard link on the filesystem. /// /// Wrapper for [`tokio::fs::hard_link`]. #[cfg_attr(docsrs, doc(cfg(feature = "tokio")))] pub async fn hard_link(src: impl AsRef, dst: impl AsRef) -> io::Result<()> { let (src, dst) = (src.as_ref(), dst.as_ref()); tokio::fs::hard_link(src, dst) .await .map_err(|err| SourceDestError::build(err, SourceDestErrorKind::HardLink, src, dst)) } /// Given a path, queries the file system to get information about a file, /// directory, etc. /// /// Wrapper for [`tokio::fs::metadata`]. #[cfg_attr(docsrs, doc(cfg(feature = "tokio")))] pub async fn metadata(path: impl AsRef) -> io::Result { let path = path.as_ref(); tokio::fs::metadata(path) .await .map_err(|err| Error::build(err, ErrorKind::Metadata, path)) } /// Reads the entire contents of a file into a bytes vector. /// /// Wrapper for [`tokio::fs::read`]. #[cfg_attr(docsrs, doc(cfg(feature = "tokio")))] pub async fn read(path: impl AsRef) -> io::Result> { let path = path.as_ref(); tokio::fs::read(path) .await .map_err(|err| Error::build(err, ErrorKind::Read, path)) } /// Reads a symbolic link, returning the file that the link points to. /// /// Wrapper for [`tokio::fs::read_link`]. #[cfg_attr(docsrs, doc(cfg(feature = "tokio")))] pub async fn read_link(path: impl AsRef) -> io::Result { let path = path.as_ref(); tokio::fs::read_link(path) .await .map_err(|err| Error::build(err, ErrorKind::ReadLink, path)) } /// Creates a future which will open a file for reading and read the entire /// contents into a string and return said string. /// /// Wrapper for [`tokio::fs::read_to_string`]. #[cfg_attr(docsrs, doc(cfg(feature = "tokio")))] pub async fn read_to_string(path: impl AsRef) -> io::Result { let path = path.as_ref(); tokio::fs::read_to_string(path) .await .map_err(|err| Error::build(err, ErrorKind::Read, path)) } /// Removes an existing, empty directory. /// /// Wrapper for [`tokio::fs::remove_dir`]. #[cfg_attr(docsrs, doc(cfg(feature = "tokio")))] pub async fn remove_dir(path: impl AsRef) -> io::Result<()> { let path = path.as_ref(); tokio::fs::remove_dir(path) .await .map_err(|err| Error::build(err, ErrorKind::RemoveDir, path)) } /// Removes a directory at this path, after removing all its contents. Use carefully! /// /// Wrapper for [`tokio::fs::remove_dir_all`]. #[cfg_attr(docsrs, doc(cfg(feature = "tokio")))] pub async fn remove_dir_all(path: impl AsRef) -> io::Result<()> { let path = path.as_ref(); tokio::fs::remove_dir_all(path) .await .map_err(|err| Error::build(err, ErrorKind::RemoveDir, path)) } /// Removes a file from the filesystem. /// /// Wrapper for [`tokio::fs::remove_file`]. #[cfg_attr(docsrs, doc(cfg(feature = "tokio")))] pub async fn remove_file(path: impl AsRef) -> io::Result<()> { let path = path.as_ref(); tokio::fs::remove_file(path) .await .map_err(|err| Error::build(err, ErrorKind::RemoveFile, path)) } /// Renames a file or directory to a new name, replacing the original file if /// `to` already exists. /// /// Wrapper for [`tokio::fs::rename`]. #[cfg_attr(docsrs, doc(cfg(feature = "tokio")))] pub async fn rename(from: impl AsRef, to: impl AsRef) -> io::Result<()> { let (from, to) = (from.as_ref(), to.as_ref()); tokio::fs::rename(from, to) .await .map_err(|err| SourceDestError::build(err, SourceDestErrorKind::Rename, from, to)) } /// Changes the permissions found on a file or a directory. /// /// Wrapper for [`tokio::fs::set_permissions`]. #[cfg_attr(docsrs, doc(cfg(feature = "tokio")))] pub async fn set_permissions(path: impl AsRef, perm: Permissions) -> io::Result<()> { let path = path.as_ref(); tokio::fs::set_permissions(path, perm) .await .map_err(|err| Error::build(err, ErrorKind::SetPermissions, path)) } /// Queries the file system metadata for a path. /// /// Wrapper for [`tokio::fs::symlink_metadata`]. #[cfg_attr(docsrs, doc(cfg(feature = "tokio")))] pub async fn symlink_metadata(path: impl AsRef) -> io::Result { let path = path.as_ref(); tokio::fs::symlink_metadata(path) .await .map_err(|err| Error::build(err, ErrorKind::SymlinkMetadata, path)) } /// Creates a new symbolic link on the filesystem. /// /// Wrapper for [`tokio::fs::symlink`]. #[cfg(unix)] #[cfg_attr(docsrs, doc(cfg(feature = "tokio")))] pub async fn symlink(src: impl AsRef, dst: impl AsRef) -> io::Result<()> { let (src, dst) = (src.as_ref(), dst.as_ref()); tokio::fs::symlink(src, dst) .await .map_err(|err| SourceDestError::build(err, SourceDestErrorKind::Symlink, src, dst)) } /// Creates a new directory symlink on the filesystem. /// /// Wrapper for [`tokio::fs::symlink_dir`]. #[cfg(windows)] #[cfg_attr(docsrs, doc(cfg(feature = "tokio")))] #[deprecated = "use fs_err::tokio::symlink_dir instead"] pub async fn symlink(src: impl AsRef, dst: impl AsRef) -> io::Result<()> { symlink_dir(src, dst).await } /// Creates a new directory symlink on the filesystem. /// /// Wrapper for [`tokio::fs::symlink_dir`]. #[cfg(windows)] #[cfg_attr(docsrs, doc(cfg(feature = "tokio")))] pub async fn symlink_dir(src: impl AsRef, dst: impl AsRef) -> io::Result<()> { let (src, dst) = (src.as_ref(), dst.as_ref()); tokio::fs::symlink_dir(src, dst) .await .map_err(|err| SourceDestError::build(err, SourceDestErrorKind::SymlinkDir, src, dst)) } /// Creates a new file symbolic link on the filesystem. /// /// Wrapper for [`tokio::fs::symlink_file`]. #[cfg(windows)] #[cfg_attr(docsrs, doc(cfg(feature = "tokio")))] pub async fn symlink_file(src: impl AsRef, dst: impl AsRef) -> io::Result<()> { let (src, dst) = (src.as_ref(), dst.as_ref()); tokio::fs::symlink_file(src, dst) .await .map_err(|err| SourceDestError::build(err, SourceDestErrorKind::SymlinkFile, src, dst)) } /// Creates a future that will open a file for writing and write the entire /// contents of `contents` to it. /// /// Wrapper for [`tokio::fs::write`]. #[cfg_attr(docsrs, doc(cfg(feature = "tokio")))] pub async fn write(path: impl AsRef, contents: impl AsRef<[u8]>) -> io::Result<()> { let (path, contents) = (path.as_ref(), contents.as_ref()); tokio::fs::write(path, contents) .await .map_err(|err| Error::build(err, ErrorKind::Write, path)) } fs-err-2.11.0/src/tokio/open_options.rs000064400000000000000000000072530072674642500161400ustar 00000000000000use crate::errors::{Error, ErrorKind}; use crate::tokio::File; use std::io; use std::path::Path; use tokio::fs::OpenOptions as TokioOpenOptions; /// Options and flags which can be used to configure how a file is opened. /// /// This is a wrapper around [`tokio::fs::OpenOptions`]. #[derive(Clone, Debug, Default)] #[cfg_attr(docsrs, doc(cfg(feature = "tokio")))] pub struct OpenOptions { tokio: TokioOpenOptions, } impl OpenOptions { /// Creates a blank new set of options ready for configuration. /// /// All options are initially set to `false`. /// /// This is a wrapped version of [`tokio::fs::OpenOptions::new`] /// /// # Examples /// /// ```no_run /// use fs_err::tokio::OpenOptions; /// /// let mut options = OpenOptions::new(); /// let future = options.read(true).open("foo.txt"); /// ``` pub fn new() -> OpenOptions { OpenOptions { tokio: TokioOpenOptions::new(), } } /// Sets the option for read access. /// /// Wrapper for [`tokio::fs::OpenOptions::read`]. pub fn read(&mut self, read: bool) -> &mut OpenOptions { self.tokio.read(read); self } /// Sets the option for write access. /// /// Wrapper for [`tokio::fs::OpenOptions::write`]. pub fn write(&mut self, write: bool) -> &mut OpenOptions { self.tokio.write(write); self } /// Sets the option for the append mode. /// /// Wrapper for [`tokio::fs::OpenOptions::append`]. pub fn append(&mut self, append: bool) -> &mut OpenOptions { self.tokio.append(append); self } /// Sets the option for truncating a previous file. /// /// Wrapper for [`tokio::fs::OpenOptions::truncate`]. pub fn truncate(&mut self, truncate: bool) -> &mut OpenOptions { self.tokio.truncate(truncate); self } /// Sets the option for creating a new file. /// /// Wrapper for [`tokio::fs::OpenOptions::create`]. pub fn create(&mut self, create: bool) -> &mut OpenOptions { self.tokio.create(create); self } /// Sets the option to always create a new file. /// /// Wrapper for [`tokio::fs::OpenOptions::create_new`]. pub fn create_new(&mut self, create_new: bool) -> &mut OpenOptions { self.tokio.create_new(create_new); self } /// Opens a file at `path` with the options specified by `self`. /// /// Wrapper for [`tokio::fs::OpenOptions::open`]. pub async fn open(&self, path: impl AsRef) -> io::Result { let path = path.as_ref(); self.tokio .open(path) .await .map(|f| File::from_parts(f, path)) .map_err(|err| Error::build(err, ErrorKind::OpenFile, path)) } } #[cfg(unix)] impl OpenOptions { /// Sets the mode bits that a new file will be created with. /// /// Wrapper for [`tokio::fs::OpenOptions::mode`]. pub fn mode(&mut self, mode: u32) -> &mut OpenOptions { self.tokio.mode(mode); self } /// Passes custom flags to the `flags` argument of `open`. /// /// Wrapper for [`tokio::fs::OpenOptions::custom_flags`]. pub fn custom_flags(&mut self, flags: i32) -> &mut OpenOptions { self.tokio.custom_flags(flags); self } } impl From for OpenOptions { fn from(std: std::fs::OpenOptions) -> Self { OpenOptions { tokio: std.into() } } } impl From for OpenOptions { fn from(tokio: TokioOpenOptions) -> Self { OpenOptions { tokio } } } fs-err-2.11.0/src/tokio/read_dir.rs000064400000000000000000000067240072674642500151770ustar 00000000000000use crate::errors::{Error, ErrorKind}; use std::ffi::OsString; use std::fs::{FileType, Metadata}; use std::io; use std::path::{Path, PathBuf}; use std::task::{ready, Context, Poll}; use tokio::fs; /// Returns a stream over the entries within a directory. /// /// Wrapper for [`tokio::fs::read_dir`]. #[cfg_attr(docsrs, doc(cfg(feature = "tokio")))] pub async fn read_dir(path: impl AsRef) -> io::Result { let path = path.as_ref(); let tokio = fs::read_dir(path) .await .map_err(|err| Error::build(err, ErrorKind::ReadDir, path))?; Ok(ReadDir { tokio, path: path.to_owned(), }) } /// Reads the entries in a directory. /// /// This is a wrapper around [`tokio::fs::ReadDir`]. #[derive(Debug)] #[must_use = "streams do nothing unless polled"] #[cfg_attr(docsrs, doc(cfg(feature = "tokio")))] pub struct ReadDir { tokio: fs::ReadDir, path: PathBuf, } impl ReadDir { /// Returns the next entry in the directory stream. /// /// Wrapper around [`tokio::fs::ReadDir::next_entry`]. pub async fn next_entry(&mut self) -> io::Result> { match self.tokio.next_entry().await { Ok(entry) => Ok(entry.map(|e| DirEntry { tokio: e })), Err(err) => Err(Error::build(err, ErrorKind::ReadDir, &self.path)), } } /// Polls for the next directory entry in the stream. /// /// Wrapper around [`tokio::fs::ReadDir::poll_next_entry`]. pub fn poll_next_entry(&mut self, cx: &mut Context<'_>) -> Poll>> { Poll::Ready(match ready!(self.tokio.poll_next_entry(cx)) { Ok(entry) => Ok(entry.map(|e| DirEntry { tokio: e })), Err(err) => Err(Error::build(err, ErrorKind::ReadDir, &self.path)), }) } } /// Entries returned by the [`ReadDir`] stream. /// /// This is a wrapper around [`tokio::fs::DirEntry`]. #[derive(Debug)] #[cfg_attr(docsrs, doc(cfg(feature = "tokio")))] pub struct DirEntry { tokio: fs::DirEntry, } impl DirEntry { /// Returns the full path to the file that this entry represents. /// /// Wrapper around [`tokio::fs::DirEntry::path`]. pub fn path(&self) -> PathBuf { self.tokio.path() } /// Returns the bare file name of this directory entry without any other /// leading path component. /// /// Wrapper around [`tokio::fs::DirEntry::file_name`]. pub fn file_name(&self) -> OsString { self.tokio.file_name() } /// Returns the metadata for the file that this entry points at. /// /// Wrapper around [`tokio::fs::DirEntry::metadata`]. pub async fn metadata(&self) -> io::Result { self.tokio .metadata() .await .map_err(|err| Error::build(err, ErrorKind::Metadata, self.path())) } /// Returns the file type for the file that this entry points at. /// /// Wrapper around [`tokio::fs::DirEntry::file_type`]. pub async fn file_type(&self) -> io::Result { self.tokio .file_type() .await .map_err(|err| Error::build(err, ErrorKind::Metadata, self.path())) } } #[cfg(unix)] impl DirEntry { /// Returns the underlying `d_ino` field in the contained `dirent` structure. /// /// Wrapper around [`tokio::fs::DirEntry::ino`]. pub fn ino(&self) -> u64 { self.tokio.ino() } }