diff --git a/src/uu/numfmt/src/errors.rs b/src/uu/numfmt/src/errors.rs new file mode 100644 index 000000000..400fc4619 --- /dev/null +++ b/src/uu/numfmt/src/errors.rs @@ -0,0 +1,34 @@ +use std::{ + error::Error, + fmt::{Debug, Display}, +}; +use uucore::error::UError; + +#[derive(Debug)] +pub enum NumfmtError { + IoError(String), + IllegalArgument(String), + FormattingError(String), +} + +impl UError for NumfmtError { + fn code(&self) -> i32 { + match self { + NumfmtError::IoError(_) => 1, + NumfmtError::IllegalArgument(_) => 1, + NumfmtError::FormattingError(_) => 2, + } + } +} + +impl Error for NumfmtError {} + +impl Display for NumfmtError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + NumfmtError::IoError(s) + | NumfmtError::IllegalArgument(s) + | NumfmtError::FormattingError(s) => write!(f, "{}", s), + } + } +} diff --git a/src/uu/numfmt/src/numfmt.rs b/src/uu/numfmt/src/numfmt.rs index b84b9ab1b..22e7210f1 100644 --- a/src/uu/numfmt/src/numfmt.rs +++ b/src/uu/numfmt/src/numfmt.rs @@ -7,15 +7,17 @@ // spell-checker:ignore N'th M'th +use crate::errors::*; use crate::format::format_and_print; use crate::options::*; use crate::units::{Result, Unit}; use clap::{crate_version, App, AppSettings, Arg, ArgMatches}; use std::io::{BufRead, Write}; use uucore::display::Quotable; -use uucore::error::{UResult, USimpleError}; +use uucore::error::UResult; use uucore::ranges::Range; +pub mod errors; pub mod format; pub mod options; mod units; @@ -53,28 +55,35 @@ fn usage() -> String { format!("{0} [OPTION]... [NUMBER]...", uucore::execution_phrase()) } -fn handle_args<'a>(args: impl Iterator, options: NumfmtOptions) -> Result<()> { +fn handle_args<'a>(args: impl Iterator, options: NumfmtOptions) -> UResult<()> { for l in args { - format_and_print(l, &options)?; + match format_and_print(l, &options) { + Ok(_) => Ok(()), + Err(e) => Err(NumfmtError::FormattingError(e.to_string())), + }?; } Ok(()) } -fn handle_stdin(options: NumfmtOptions) -> Result<()> { +fn handle_stdin(options: NumfmtOptions) -> UResult<()> { let stdin = std::io::stdin(); let locked_stdin = stdin.lock(); let mut lines = locked_stdin.lines(); - for l in lines.by_ref().take(options.header) { - l.map(|s| println!("{}", s)).map_err(|e| e.to_string())?; + for (idx, line) in lines.by_ref().enumerate() { + match line { + Ok(l) if idx < options.header => { + println!("{}", l); + Ok(()) + } + Ok(l) => match format_and_print(&l, &options) { + Ok(_) => Ok(()), + Err(e) => Err(NumfmtError::FormattingError(e.to_string())), + }, + Err(e) => Err(NumfmtError::IoError(e.to_string())), + }?; } - - for l in lines { - l.map_err(|e| e.to_string()) - .and_then(|l| format_and_print(&l, &options))?; - } - Ok(()) } @@ -161,18 +170,17 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { let matches = uu_app().usage(&usage[..]).get_matches_from(args); - let result = - parse_options(&matches).and_then(|options| match matches.values_of(options::NUMBER) { - Some(values) => handle_args(values, options), - None => handle_stdin(options), - }); + let options = parse_options(&matches).map_err(NumfmtError::IllegalArgument)?; + + let result = match matches.values_of(options::NUMBER) { + Some(values) => handle_args(values, options), + None => handle_stdin(options), + }; match result { Err(e) => { std::io::stdout().flush().expect("error flushing stdout"); - // TODO Change `handle_args()` and `handle_stdin()` so that - // they return `UResult`. - return Err(USimpleError::new(1, e)); + Err(e) } _ => Ok(()), } diff --git a/tests/by-util/test_numfmt.rs b/tests/by-util/test_numfmt.rs index 596aab6ba..9b1b91ed7 100644 --- a/tests/by-util/test_numfmt.rs +++ b/tests/by-util/test_numfmt.rs @@ -568,3 +568,8 @@ fn test_suffix_with_padding() { .succeeds() .stdout_only(" 1000pad 2000 3000\n"); } + +#[test] +fn test_invalid_number_returns_status_2() { + new_ucmd!().pipe_in("hello").fails().code_is(2); +}