From 8a552055213278a238b2fa1c86840bcd87ce99ef Mon Sep 17 00:00:00 2001 From: jfinkels Date: Wed, 29 Dec 2021 09:20:17 -0500 Subject: [PATCH] seq: return UResult from uumain() function (#2784) --- src/uu/seq/src/error.rs | 70 ++++++++++++++++++++++++++++ src/uu/seq/src/seq.rs | 97 +++++++++++++-------------------------- tests/by-util/test_seq.rs | 17 +++++++ 3 files changed, 119 insertions(+), 65 deletions(-) create mode 100644 src/uu/seq/src/error.rs diff --git a/src/uu/seq/src/error.rs b/src/uu/seq/src/error.rs new file mode 100644 index 000000000..837cd5c33 --- /dev/null +++ b/src/uu/seq/src/error.rs @@ -0,0 +1,70 @@ +// * This file is part of the uutils coreutils package. +// * +// * For the full copyright and license information, please view the LICENSE +// * file that was distributed with this source code. +// spell-checker:ignore numberparse argtype +//! Errors returned by seq. +use std::error::Error; +use std::fmt::Display; + +use uucore::display::Quotable; +use uucore::error::UError; + +use crate::numberparse::ParseNumberError; + +#[derive(Debug)] +pub enum SeqError { + /// An error parsing the input arguments. + /// + /// The parameters are the [`String`] argument as read from the + /// command line and the underlying parsing error itself. + ParseError(String, ParseNumberError), + + /// The increment argument was zero, which is not allowed. + /// + /// The parameter is the increment argument as a [`String`] as read + /// from the command line. + ZeroIncrement(String), +} + +impl SeqError { + /// The [`String`] argument as read from the command-line. + fn arg(&self) -> &str { + match self { + SeqError::ParseError(s, _) => s, + SeqError::ZeroIncrement(s) => s, + } + } + + /// The type of argument that is causing the error. + fn argtype(&self) -> &str { + match self { + SeqError::ParseError(_, e) => match e { + ParseNumberError::Float => "floating point", + ParseNumberError::Nan => "'not-a-number'", + ParseNumberError::Hex => "hexadecimal", + }, + SeqError::ZeroIncrement(_) => "Zero increment", + } + } +} +impl UError for SeqError { + /// Always return 1. + fn code(&self) -> i32 { + 1 + } +} + +impl Error for SeqError {} + +impl Display for SeqError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "invalid {} argument: {}\nTry '{} --help' for more information.", + self.argtype(), + self.arg().quote(), + uucore::execution_phrase(), + ) + } +} diff --git a/src/uu/seq/src/seq.rs b/src/uu/seq/src/seq.rs index 75e9b1598..556ef9a6d 100644 --- a/src/uu/seq/src/seq.rs +++ b/src/uu/seq/src/seq.rs @@ -1,26 +1,27 @@ -// TODO: Make -w flag work with decimals +// * This file is part of the uutils coreutils package. +// * +// * For the full copyright and license information, please view the LICENSE +// * file that was distributed with this source code. // TODO: Support -f flag - // spell-checker:ignore (ToDO) istr chiter argptr ilen extendedbigdecimal extendedbigint numberparse - -#[macro_use] -extern crate uucore; +use std::io::{stdout, ErrorKind, Write}; use clap::{crate_version, App, AppSettings, Arg}; use num_traits::Zero; -use std::io::{stdout, ErrorKind, Write}; +use uucore::error::FromIo; +use uucore::error::UResult; + +mod error; mod extendedbigdecimal; mod extendedbigint; mod number; mod numberparse; +use crate::error::SeqError; use crate::extendedbigdecimal::ExtendedBigDecimal; use crate::extendedbigint::ExtendedBigInt; use crate::number::Number; use crate::number::PreciseNumber; -use crate::numberparse::ParseNumberError; - -use uucore::display::Quotable; static ABOUT: &str = "Display numbers from FIRST to LAST, in steps of INCREMENT."; static OPT_SEPARATOR: &str = "separator"; @@ -54,47 +55,8 @@ type RangeInt = (ExtendedBigInt, ExtendedBigInt, ExtendedBigInt); /// The elements are (first, increment, last). type RangeFloat = (ExtendedBigDecimal, ExtendedBigDecimal, ExtendedBigDecimal); -/// Terminate the process with error code 1. -/// -/// Before terminating the process, this function prints an error -/// message that depends on `arg` and `e`. -/// -/// Although the signature of this function states that it returns a -/// [`PreciseNumber`], it never reaches the return statement. It is just -/// there to make it easier to use this function when unwrapping the -/// result of calling [`str::parse`] when attempting to parse a -/// [`PreciseNumber`]. -/// -/// # Examples -/// -/// ```rust,ignore -/// let s = "1.2e-3"; -/// s.parse::.unwrap_or_else(|e| exit_with_error(s, e)) -/// ``` -fn exit_with_error(arg: &str, e: ParseNumberError) -> ! { - match e { - ParseNumberError::Float => crash!( - 1, - "invalid floating point argument: {}\nTry '{} --help' for more information.", - arg.quote(), - uucore::execution_phrase() - ), - ParseNumberError::Nan => crash!( - 1, - "invalid 'not-a-number' argument: {}\nTry '{} --help' for more information.", - arg.quote(), - uucore::execution_phrase() - ), - ParseNumberError::Hex => crash!( - 1, - "invalid hexadecimal argument: {}\nTry '{} --help' for more information.", - arg.quote(), - uucore::execution_phrase() - ), - } -} - -pub fn uumain(args: impl uucore::Args) -> i32 { +#[uucore_procs::gen_uumain] +pub fn uumain(args: impl uucore::Args) -> UResult<()> { let usage = usage(); let matches = uu_app().usage(&usage[..]).get_matches_from(args); @@ -107,28 +69,33 @@ pub fn uumain(args: impl uucore::Args) -> i32 { }; let first = if numbers.len() > 1 { - let slice = numbers[0]; - slice.parse().unwrap_or_else(|e| exit_with_error(slice, e)) + match numbers[0].parse() { + Ok(num) => num, + Err(e) => return Err(SeqError::ParseError(numbers[0].to_string(), e).into()), + } } else { PreciseNumber::one() }; let increment = if numbers.len() > 2 { - let slice = numbers[1]; - slice.parse().unwrap_or_else(|e| exit_with_error(slice, e)) + match numbers[1].parse() { + Ok(num) => num, + Err(e) => return Err(SeqError::ParseError(numbers[1].to_string(), e).into()), + } } else { PreciseNumber::one() }; if increment.is_zero() { - show_error!( - "invalid Zero increment value: '{}'\nTry '{} --help' for more information.", - numbers[1], - uucore::execution_phrase() - ); - return 1; + return Err(SeqError::ZeroIncrement(numbers[1].to_string()).into()); } let last: PreciseNumber = { - let slice = numbers[numbers.len() - 1]; - slice.parse().unwrap_or_else(|e| exit_with_error(slice, e)) + // We are guaranteed that `numbers.len()` is greater than zero + // and at most three because of the argument specification in + // `uu_app()`. + let n: usize = numbers.len(); + match numbers[n - 1].parse() { + Ok(num) => num, + Err(e) => return Err(SeqError::ParseError(numbers[n - 1].to_string(), e).into()), + } }; let padding = first @@ -164,9 +131,9 @@ pub fn uumain(args: impl uucore::Args) -> i32 { ), }; match result { - Ok(_) => 0, - Err(err) if err.kind() == ErrorKind::BrokenPipe => 0, - Err(_) => 1, + Ok(_) => Ok(()), + Err(err) if err.kind() == ErrorKind::BrokenPipe => Ok(()), + Err(e) => Err(e.map_err_context(|| "write error".into())), } } diff --git a/tests/by-util/test_seq.rs b/tests/by-util/test_seq.rs index e58ac3a2a..e6f4bce0b 100644 --- a/tests/by-util/test_seq.rs +++ b/tests/by-util/test_seq.rs @@ -1,3 +1,4 @@ +// spell-checker:ignore lmnop xlmnop use crate::common::util::*; use std::io::Read; @@ -676,3 +677,19 @@ fn test_rounding_end() { .stdout_is("1\n") .no_stderr(); } + +#[test] +fn test_parse_error_float() { + new_ucmd!() + .arg("lmnop") + .fails() + .usage_error("invalid floating point argument: 'lmnop'"); +} + +#[test] +fn test_parse_error_hex() { + new_ucmd!() + .arg("0xlmnop") + .fails() + .usage_error("invalid hexadecimal argument: '0xlmnop'"); +}