From 305be094032a66bad90809c5b56a0642a904f318 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Fri, 21 Mar 2025 17:18:40 +0100 Subject: [PATCH] ls: move to thiserror --- src/uu/ls/Cargo.toml | 1 + src/uu/ls/src/ls.rs | 142 +++++++++++-------------------------------- 2 files changed, 38 insertions(+), 105 deletions(-) diff --git a/src/uu/ls/Cargo.toml b/src/uu/ls/Cargo.toml index ef7452469..566f558dd 100644 --- a/src/uu/ls/Cargo.toml +++ b/src/uu/ls/Cargo.toml @@ -26,6 +26,7 @@ lscolors = { workspace = true } number_prefix = { workspace = true } selinux = { workspace = true, optional = true } terminal_size = { workspace = true } +thiserror = { workspace = true } uucore = { workspace = true, features = [ "colors", "custom-tz-fmt", diff --git a/src/uu/ls/src/ls.rs b/src/uu/ls/src/ls.rs index 3765fc5b6..4d9899169 100644 --- a/src/uu/ls/src/ls.rs +++ b/src/uu/ls/src/ls.rs @@ -10,9 +10,8 @@ use std::os::windows::fs::MetadataExt; use std::{cell::OnceCell, num::IntErrorKind}; use std::{ cmp::Reverse, - error::Error, ffi::{OsStr, OsString}, - fmt::{Display, Write as FmtWrite}, + fmt::Write as FmtWrite, fs::{self, DirEntry, FileType, Metadata, ReadDir}, io::{stdout, BufWriter, ErrorKind, Stdout, Write}, path::{Path, PathBuf}, @@ -35,7 +34,7 @@ use clap::{ use glob::{MatchOptions, Pattern}; use lscolors::LsColors; use term_grid::{Direction, Filling, Grid, GridOptions}; - +use thiserror::Error; use uucore::error::USimpleError; use uucore::format::human::{human_readable, SizeFormat}; #[cfg(all(unix, not(any(target_os = "android", target_os = "macos"))))] @@ -175,14 +174,41 @@ const POSIXLY_CORRECT_BLOCK_SIZE: u64 = 512; const DEFAULT_BLOCK_SIZE: u64 = 1024; const DEFAULT_FILE_SIZE_BLOCK_SIZE: u64 = 1; -#[derive(Debug)] +#[derive(Error, Debug)] enum LsError { + #[error("invalid line width: '{0}'")] InvalidLineWidth(String), - IOError(std::io::Error), - IOErrorContext(std::io::Error, PathBuf, bool), + + #[error("general io error: {0}")] + IOError(#[from] std::io::Error), + + #[error("{}", match .1.kind() { + ErrorKind::NotFound => format!("cannot access '{}': No such file or directory", .0.to_string_lossy()), + ErrorKind::PermissionDenied => match .1.raw_os_error().unwrap_or(1) { + 1 => format!("cannot access '{}': Operation not permitted", .0.to_string_lossy()), + _ => if .0.is_dir() { + format!("cannot open directory '{}': Permission denied", .0.to_string_lossy()) + } else { + format!("cannot open file '{}': Permission denied", .0.to_string_lossy()) + }, + }, + _ => match .1.raw_os_error().unwrap_or(1) { + 9 => format!("cannot open directory '{}': Bad file descriptor", .0.to_string_lossy()), + _ => format!("unknown io error: '{:?}', '{:?}'", .0.to_string_lossy(), .1), + }, + })] + IOErrorContext(PathBuf, std::io::Error, bool), + + #[error("invalid --block-size argument '{0}'")] BlockSizeParseError(String), + + #[error("--dired and --zero are incompatible")] DiredAndZeroAreIncompatible, + + #[error("{}: not listing already-listed directory", .0.to_string_lossy())] AlreadyListedError(PathBuf), + + #[error("invalid --time-style argument {0}\nPossible values are: {1:?}\n\nFor more information try --help")] TimeStyleParseError(String, Vec), } @@ -201,100 +227,6 @@ impl UError for LsError { } } -impl Error for LsError {} - -impl Display for LsError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::BlockSizeParseError(s) => { - write!(f, "invalid --block-size argument {}", s.quote()) - } - Self::DiredAndZeroAreIncompatible => { - write!(f, "--dired and --zero are incompatible") - } - Self::TimeStyleParseError(s, possible_time_styles) => { - write!( - f, - "invalid --time-style argument {}\nPossible values are: {:?}\n\nFor more information try --help", - s.quote(), - possible_time_styles - ) - } - Self::InvalidLineWidth(s) => write!(f, "invalid line width: {}", s.quote()), - Self::IOError(e) => write!(f, "general io error: {e}"), - Self::IOErrorContext(e, p, _) => { - let error_kind = e.kind(); - let errno = e.raw_os_error().unwrap_or(1i32); - - match error_kind { - // No such file or directory - ErrorKind::NotFound => { - write!( - f, - "cannot access '{}': No such file or directory", - p.to_string_lossy(), - ) - } - // Permission denied and Operation not permitted - ErrorKind::PermissionDenied => - { - #[allow(clippy::wildcard_in_or_patterns)] - match errno { - 1i32 => { - write!( - f, - "cannot access '{}': Operation not permitted", - p.to_string_lossy(), - ) - } - 13i32 | _ => { - if p.is_dir() { - write!( - f, - "cannot open directory '{}': Permission denied", - p.to_string_lossy(), - ) - } else { - write!( - f, - "cannot open file '{}': Permission denied", - p.to_string_lossy(), - ) - } - } - } - } - _ => match errno { - 9i32 => { - // only should ever occur on a read_dir on a bad fd - write!( - f, - "cannot open directory '{}': Bad file descriptor", - p.to_string_lossy(), - ) - } - _ => { - write!( - f, - "unknown io error: '{:?}', '{:?}'", - p.to_string_lossy(), - e - ) - } - }, - } - } - Self::AlreadyListedError(path) => { - write!( - f, - "{}: not listing already-listed directory", - path.to_string_lossy() - ) - } - } - } -} - #[derive(PartialEq, Eq, Debug)] pub enum Format { Columns, @@ -2054,8 +1986,8 @@ impl PathData { } } show!(LsError::IOErrorContext( - err, self.p_buf.clone(), + err, self.command_line )); None @@ -2161,8 +2093,8 @@ pub fn list(locs: Vec<&Path>, config: &Config) -> UResult<()> { // flush stdout buffer before the error to preserve formatting and order out.flush()?; show!(LsError::IOErrorContext( - err, path_data.p_buf.clone(), + err, path_data.command_line )); continue; @@ -2396,8 +2328,8 @@ fn enter_directory( Err(err) => { out.flush()?; show!(LsError::IOErrorContext( - err, e.p_buf.clone(), + err, e.command_line )); continue; @@ -3365,7 +3297,7 @@ fn display_item_name( } } Err(err) => { - show!(LsError::IOErrorContext(err, path.p_buf.clone(), false)); + show!(LsError::IOErrorContext(path.p_buf.clone(), err, false)); } } } @@ -3448,7 +3380,7 @@ fn get_security_context(config: &Config, p_buf: &Path, must_dereference: bool) - Err(err) => { // The Path couldn't be dereferenced, so return early and set exit code 1 // to indicate a minor error - show!(LsError::IOErrorContext(err, p_buf.to_path_buf(), false)); + show!(LsError::IOErrorContext(p_buf.to_path_buf(), err, false)); return substitute_string; } Ok(_md) => (),