1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-29 03:57:44 +00:00

Merge pull request #4060 from orhun/refactor/add_nix_error_auto_conversion

refactor: add automatic conversion for `nix::Errno`
This commit is contained in:
Sylvestre Ledru 2022-11-04 19:09:46 +01:00 committed by GitHub
commit f32f89cbf9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 94 additions and 26 deletions

View file

@ -18,6 +18,8 @@ use nix::fcntl::{open, OFlag};
use nix::sys::stat::Mode;
use std::path::Path;
use uucore::display::Quotable;
#[cfg(any(target_os = "linux", target_os = "android"))]
use uucore::error::FromIo;
use uucore::error::{UResult, USimpleError};
use uucore::format_usage;
@ -170,29 +172,14 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
// Use the Nix open to be able to set the NONBLOCK flags for fifo files
#[cfg(any(target_os = "linux", target_os = "android"))]
{
match open(Path::new(&f), OFlag::O_NONBLOCK, Mode::empty()) {
Ok(_) => {}
Err(e) => {
if e == Errno::ENOENT {
return Err(USimpleError::new(
1,
format!("cannot stat {}: No such file or directory", f.quote()),
));
}
if e == Errno::EACCES {
if Path::new(&f).is_dir() {
return Err(USimpleError::new(
1,
format!("error opening {}: Permission denied", f.quote()),
));
} else {
// ignore the issue
// ./target/debug/coreutils sync --data file
}
}
let path = Path::new(&f);
if let Err(e) = open(path, OFlag::O_NONBLOCK, Mode::empty()) {
if e != Errno::EACCES || (e == Errno::EACCES && path.is_dir()) {
return e.map_err_context(|| format!("cannot stat {}", f.quote()))?;
}
}
}
#[cfg(not(any(target_os = "linux", target_os = "android")))]
{
if !Path::new(&f).exists() {

View file

@ -38,7 +38,7 @@ os_display = "0.1.3"
[target.'cfg(unix)'.dependencies]
walkdir = { version="2.3.2", optional=true }
nix = { version = "0.25", optional = true, default-features = false, features = ["fs", "uio", "zerocopy"] }
nix = { version = "0.25", default-features = false, features = ["fs", "uio", "zerocopy"] }
[dev-dependencies]
clap = "4.0"
@ -53,8 +53,8 @@ default = []
# * non-default features
encoding = ["data-encoding", "data-encoding-macro", "z85", "thiserror"]
entries = ["libc"]
fs = ["libc", "nix", "winapi-util", "windows-sys"]
fsext = ["libc", "nix", "time", "windows-sys"]
fs = ["libc", "winapi-util", "windows-sys"]
fsext = ["libc", "time", "windows-sys"]
lines = []
memo = ["itertools"]
mode = ["libc"]
@ -65,4 +65,4 @@ signals = []
utf8 = []
utmpx = ["time", "time/macros", "libc", "dns-lookup"]
wide = []
pipes = ["nix"]
pipes = []

View file

@ -508,6 +508,60 @@ impl From<std::io::Error> for Box<dyn UError> {
}
}
/// Enables the conversion from [`Result<T, nix::Error>`] to [`UResult<T>`].
///
/// # Examples
///
/// ```
/// use uucore::error::FromIo;
/// use nix::errno::Errno;
///
/// let nix_err = Err::<(), nix::Error>(Errno::EACCES);
/// let uio_result = nix_err.map_err_context(|| String::from("fix me please!"));
///
/// // prints "fix me please!: Permission denied"
/// println!("{}", uio_result.unwrap_err());
/// ```
#[cfg(any(target_os = "linux", target_os = "android"))]
impl<T> FromIo<UResult<T>> for Result<T, nix::Error> {
fn map_err_context(self, context: impl FnOnce() -> String) -> UResult<T> {
self.map_err(|e| {
Box::new(UIoError {
context: Some((context)()),
inner: std::io::Error::from_raw_os_error(e as i32),
}) as Box<dyn UError>
})
}
}
#[cfg(any(target_os = "linux", target_os = "android"))]
impl<T> FromIo<UResult<T>> for nix::Error {
fn map_err_context(self, context: impl FnOnce() -> String) -> UResult<T> {
Err(Box::new(UIoError {
context: Some((context)()),
inner: std::io::Error::from_raw_os_error(self as i32),
}) as Box<dyn UError>)
}
}
#[cfg(any(target_os = "linux", target_os = "android"))]
impl From<nix::Error> for UIoError {
fn from(f: nix::Error) -> Self {
Self {
context: None,
inner: std::io::Error::from_raw_os_error(f as i32),
}
}
}
#[cfg(any(target_os = "linux", target_os = "android"))]
impl From<nix::Error> for Box<dyn UError> {
fn from(f: nix::Error) -> Self {
let u_error: UIoError = f.into();
Box::new(u_error) as Self
}
}
/// Shorthand to construct [`UIoError`]-instances.
///
/// This macro serves as a convenience call to quickly construct instances of
@ -693,3 +747,30 @@ impl Display for ClapErrorWrapper {
Ok(())
}
}
#[cfg(test)]
mod tests {
#[test]
#[cfg(any(target_os = "linux", target_os = "android"))]
fn test_nix_error_conversion() {
use super::{FromIo, UIoError};
use nix::errno::Errno;
use std::io::ErrorKind;
for (nix_error, expected_error_kind) in [
(Errno::EACCES, ErrorKind::PermissionDenied),
(Errno::ENOENT, ErrorKind::NotFound),
(Errno::EEXIST, ErrorKind::AlreadyExists),
] {
let error = UIoError::from(nix_error);
assert_eq!(expected_error_kind, error.inner.kind());
}
assert_eq!(
"test: Permission denied",
Err::<(), nix::Error>(Errno::EACCES)
.map_err_context(|| String::from("test"))
.unwrap_err()
.to_string()
)
}
}

View file

@ -64,9 +64,9 @@ fn test_sync_no_permission_dir() {
ts.ccmd("chmod").arg("0").arg(dir).succeeds();
let result = ts.ucmd().arg("--data").arg(dir).fails();
result.stderr_contains("sync: error opening 'foo': Permission denied");
result.stderr_contains("sync: cannot stat 'foo': Permission denied");
let result = ts.ucmd().arg(dir).fails();
result.stderr_contains("sync: error opening 'foo': Permission denied");
result.stderr_contains("sync: cannot stat 'foo': Permission denied");
}
#[cfg(not(target_os = "windows"))]