Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,12 @@ jobs:
command: test
args: --all --features unstable

- name: tests with verbose errors
uses: actions-rs/cargo@v1
with:
command: test
args: --all --features 'unstable verbose-errors'

check_fmt_and_docs:
name: Checking fmt and docs
runs-on: ubuntu-latest
Expand Down
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ std = [
"pin-utils",
"slab",
]
verbose-errors = []

[dependencies]
async-attributes = { version = "1.1.1", optional = true }
Expand Down
13 changes: 11 additions & 2 deletions src/fs/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use std::sync::{Arc, Mutex};

use crate::fs::{Metadata, Permissions};
use crate::future;
use crate::utils::VerboseErrorExt;
use crate::io::{self, Read, Seek, SeekFrom, Write};
use crate::path::Path;
use crate::prelude::*;
Expand Down Expand Up @@ -112,7 +113,11 @@ impl File {
/// ```
pub async fn open<P: AsRef<Path>>(path: P) -> io::Result<File> {
let path = path.as_ref().to_owned();
let file = spawn_blocking(move || std::fs::File::open(&path)).await?;
let file = spawn_blocking(move || {
std::fs::File::open(&path)
.verbose_context(|| format!("Could not open {}", path.display()))
})
.await?;
Ok(File::new(file, true))
}

Expand Down Expand Up @@ -147,7 +152,11 @@ impl File {
/// ```
pub async fn create<P: AsRef<Path>>(path: P) -> io::Result<File> {
let path = path.as_ref().to_owned();
let file = spawn_blocking(move || std::fs::File::create(&path)).await?;
let file = spawn_blocking(move || {
std::fs::File::create(&path)
.verbose_context(|| format!("Could not create {}", path.display()))
})
.await?;
Ok(File::new(file, true))
}

Expand Down
1 change: 1 addition & 0 deletions src/io/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,7 @@ cfg_std! {
pub(crate) mod read;
pub(crate) mod seek;
pub(crate) mod write;
pub(crate) mod utils;

mod buf_reader;
mod buf_writer;
Expand Down
52 changes: 52 additions & 0 deletions src/io/utils.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
use crate::utils::VerboseErrorExt;

/// Wrap `std::io::Error` with additional message
///
/// *Note* Only active when `verbose-errors` feature is enabled for this crate!
///
/// Keeps the original error kind and stores the original I/O error as `source`.
impl<T> VerboseErrorExt for Result<T, std::io::Error> {
#[cfg(feature = "verbose-errors")]
fn verbose_context(self, message: impl Fn() -> String) -> Self {
self.map_err(|e| verbose::Error::wrap(e, message()))
}
}

#[cfg(feature = "verbose-errors")]
mod verbose {
use std::{error::Error as StdError, fmt, io};

#[derive(Debug)]
pub(crate) struct Error {
source: io::Error,
message: String,
}

impl Error {
pub(crate) fn wrap(source: io::Error, message: impl Into<String>) -> io::Error {
io::Error::new(
source.kind(),
Error {
source,
message: message.into(),
},
)
}
}

impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.message)
}
}

impl StdError for Error {
fn description(&self) -> &str {
self.source.description()
}

fn source(&self) -> Option<&(dyn StdError + 'static)> {
Some(&self.source)
}
}
}
14 changes: 14 additions & 0 deletions src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,20 @@ pub fn random(n: u32) -> u32 {
})
}

/// Add additional context to errors
///
/// *Note for implementors:* The given closure must only be executed when
/// `verbose-errors` feature is enabled for this crate!
pub(crate) trait VerboseErrorExt: Sized {
#[cfg(feature = "verbose-errors")]
fn verbose_context(self, message: impl Fn() -> String) -> Self;

#[cfg(not(feature = "verbose-errors"))]
fn verbose_context(self, _: impl Fn() -> String) -> Self {
self
}
}

/// Defers evaluation of a block of code until the end of the scope.
#[cfg(feature = "default")]
#[doc(hidden)]
Expand Down
20 changes: 20 additions & 0 deletions tests/verbose_errors.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#[cfg(feature = "verbose-errors")]
mod verbose_tests {
use async_std::{fs, task};

#[test]
fn open_file() {
task::block_on(async {
let non_existing_file =
"/ashjudlkahasdasdsikdhajik/asdasdasdasdasdasd/fjuiklashdbflasas";
let res = fs::File::open(non_existing_file).await;
match res {
Ok(_) => panic!("Found file with random name: We live in a simulation"),
Err(e) => assert_eq!(
"Could not open /ashjudlkahasdasdsikdhajik/asdasdasdasdasdasd/fjuiklashdbflasas",
&format!("{}", e)
),
}
})
}
}