diff --git a/src/uu/ls/src/ls.rs b/src/uu/ls/src/ls.rs index 6ca3f4bbe..059981e28 100644 --- a/src/uu/ls/src/ls.rs +++ b/src/uu/ls/src/ls.rs @@ -26,10 +26,11 @@ use quoting_style::{escape_name, QuotingStyle}; use std::os::windows::fs::MetadataExt; use std::{ cmp::Reverse, + error::Error, + fmt::Display, fs::{self, DirEntry, FileType, Metadata}, io::{stdout, BufWriter, Stdout, Write}, path::{Path, PathBuf}, - process::exit, time::{SystemTime, UNIX_EPOCH}, }; #[cfg(unix)] @@ -38,8 +39,8 @@ use std::{ os::unix::fs::{FileTypeExt, MetadataExt}, time::Duration, }; - use term_grid::{Cell, Direction, Filling, Grid, GridOptions}; +use uucore::error::{set_exit_code, FromIo, UCustomError, UResult}; use unicode_width::UnicodeWidthStr; #[cfg(unix)] @@ -125,6 +126,32 @@ pub mod options { pub static IGNORE: &str = "ignore"; } +#[derive(Debug)] +enum LsError { + InvalidLineWidth(String), + NoMetadata(PathBuf), +} + +impl UCustomError for LsError { + fn code(&self) -> i32 { + match self { + LsError::InvalidLineWidth(_) => 2, + LsError::NoMetadata(_) => 1, + } + } +} + +impl Error for LsError {} + +impl Display for LsError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + LsError::InvalidLineWidth(s) => write!(f, "invalid line width: '{}'", s), + LsError::NoMetadata(p) => write!(f, "could not open file: '{}'", p.display()), + } + } +} + #[derive(PartialEq, Eq)] enum Format { Columns, @@ -218,7 +245,7 @@ struct LongFormat { impl Config { #[allow(clippy::cognitive_complexity)] - fn from(options: clap::ArgMatches) -> Config { + fn from(options: clap::ArgMatches) -> UResult { let (mut format, opt) = if let Some(format_) = options.value_of(options::FORMAT) { ( match format_ { @@ -369,15 +396,13 @@ impl Config { } }; - let width = options - .value_of(options::WIDTH) - .map(|x| { - x.parse::().unwrap_or_else(|_e| { - show_error!("invalid line width: '{}'", x); - exit(2); - }) - }) - .or_else(|| termsize::get().map(|s| s.cols)); + let width = match options.value_of(options::WIDTH) { + Some(x) => match x.parse::() { + Ok(u) => Some(u), + Err(_) => return Err(LsError::InvalidLineWidth(x.into()).into()), + }, + None => termsize::get().map(|s| s.cols), + }; #[allow(clippy::needless_bool)] let show_control = if options.is_present(options::HIDE_CONTROL_CHARS) { @@ -528,7 +553,7 @@ impl Config { Dereference::DirArgs }; - Config { + Ok(Config { format, files, sort, @@ -547,11 +572,12 @@ impl Config { quoting_style, indicator_style, time_style, - } + }) } } -pub fn uumain(args: impl uucore::Args) -> i32 { +#[uucore_procs::gen_uumain] +pub fn uumain(args: impl uucore::Args) -> UResult<()> { let args = args .collect_str(InvalidEncodingHandling::Ignore) .accept_any(); @@ -567,7 +593,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { .map(|v| v.map(ToString::to_string).collect()) .unwrap_or_else(|| vec![String::from(".")]); - list(locs, Config::from(matches)) + list(locs, Config::from(matches)?) } pub fn uu_app() -> App<'static, 'static> { @@ -1190,10 +1216,9 @@ impl PathData { } } -fn list(locs: Vec, config: Config) -> i32 { +fn list(locs: Vec, config: Config) -> UResult<()> { let mut files = Vec::::new(); let mut dirs = Vec::::new(); - let mut has_failed = false; let mut out = BufWriter::new(stdout()); @@ -1202,19 +1227,16 @@ fn list(locs: Vec, config: Config) -> i32 { let path_data = PathData::new(p, None, None, &config, true); if path_data.md().is_none() { - show_error!("'{}': {}", &loc, "No such file or directory"); - /* - We found an error, the return code of ls should not be 0 - And no need to continue the execution - */ - has_failed = true; + show!(std::io::ErrorKind::NotFound + .map_err_context(|| format!("cannot access '{}'", path_data.p_buf.display()))); + // We found an error, no need to continue the execution continue; } let show_dir_contents = match path_data.file_type() { Some(ft) => !config.directory && ft.is_dir(), None => { - has_failed = true; + set_exit_code(1); false } }; @@ -1235,11 +1257,8 @@ fn list(locs: Vec, config: Config) -> i32 { } enter_directory(&dir, &config, &mut out); } - if has_failed { - 1 - } else { - 0 - } + + Ok(()) } fn sort_entries(entries: &mut Vec, config: &Config) { @@ -1478,7 +1497,7 @@ fn display_item_long( ) { let md = match item.md() { None => { - show_error!("could not show file: {}", &item.p_buf.display()); + show!(LsError::NoMetadata(item.p_buf.clone())); return; } Some(md) => md,