mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-29 12:07:46 +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:
commit
f32f89cbf9
4 changed files with 94 additions and 26 deletions
|
@ -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() {
|
||||
|
|
|
@ -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 = []
|
||||
|
|
|
@ -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()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"))]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue