1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-27 19:17:43 +00:00

tac: return UResult from uumain() function

This commit is contained in:
Jeffrey Finkelstein 2021-12-19 17:46:15 -05:00
parent 5c0adb26a5
commit b8c572b32d
2 changed files with 100 additions and 29 deletions

60
src/uu/tac/src/error.rs Normal file
View file

@ -0,0 +1,60 @@
// * 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.
//! Errors returned by tac during processing of a file.
use std::error::Error;
use std::fmt::Display;
use uucore::display::Quotable;
use uucore::error::UError;
#[derive(Debug)]
pub enum TacError {
/// A regular expression given by the user is invalid.
InvalidRegex(regex::Error),
/// An argument to tac is invalid.
InvalidArgument(String),
/// The specified file is not found on the filesystem.
FileNotFound(String),
/// An error reading the contents of a file or stdin.
///
/// The parameters are the name of the file and the underlying
/// [`std::io::Error`] that caused this error.
ReadError(String, std::io::Error),
/// An error writing the (reversed) contents of a file or stdin.
///
/// The parameter is the underlying [`std::io::Error`] that caused
/// this error.
WriteError(std::io::Error),
}
impl UError for TacError {
fn code(&self) -> i32 {
1
}
}
impl Error for TacError {}
impl Display for TacError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
TacError::InvalidRegex(e) => write!(f, "invalid regular expression: {}", e),
TacError::InvalidArgument(s) => {
write!(f, "{}: read error: Invalid argument", s.maybe_quote())
}
TacError::FileNotFound(s) => write!(
f,
"failed to open {} for reading: No such file or directory",
s.quote()
),
TacError::ReadError(s, e) => write!(f, "failed to read from {}: {}", s, e),
TacError::WriteError(e) => write!(f, "failed to write to stdout: {}", e),
}
}
}

View file

@ -6,9 +6,7 @@
// * file that was distributed with this source code. // * file that was distributed with this source code.
// spell-checker:ignore (ToDO) sbytes slen dlen memmem memmap Mmap mmap SIGBUS // spell-checker:ignore (ToDO) sbytes slen dlen memmem memmap Mmap mmap SIGBUS
mod error;
#[macro_use]
extern crate uucore;
use clap::{crate_version, App, Arg}; use clap::{crate_version, App, Arg};
use memchr::memmem; use memchr::memmem;
@ -19,8 +17,13 @@ use std::{
path::Path, path::Path,
}; };
use uucore::display::Quotable; use uucore::display::Quotable;
use uucore::error::UError;
use uucore::error::UResult;
use uucore::show;
use uucore::InvalidEncodingHandling; use uucore::InvalidEncodingHandling;
use crate::error::TacError;
static NAME: &str = "tac"; static NAME: &str = "tac";
static USAGE: &str = "[OPTION]... [FILE]..."; static USAGE: &str = "[OPTION]... [FILE]...";
static SUMMARY: &str = "Write each file to standard output, last line first."; static SUMMARY: &str = "Write each file to standard output, last line first.";
@ -32,7 +35,8 @@ mod options {
pub static FILE: &str = "file"; pub static FILE: &str = "file";
} }
pub fn uumain(args: impl uucore::Args) -> i32 { #[uucore_procs::gen_uumain]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let args = args let args = args
.collect_str(InvalidEncodingHandling::ConvertLossy) .collect_str(InvalidEncodingHandling::ConvertLossy)
.accept_any(); .accept_any();
@ -214,11 +218,13 @@ fn buffer_tac(data: &[u8], before: bool, separator: &str) -> std::io::Result<()>
Ok(()) Ok(())
} }
fn tac(filenames: Vec<&str>, before: bool, regex: bool, separator: &str) -> i32 { fn tac(filenames: Vec<&str>, before: bool, regex: bool, separator: &str) -> UResult<()> {
let mut exit_code = 0; // Compile the regular expression pattern if it is provided.
let maybe_pattern = if regex {
let pattern = if regex { match regex::bytes::Regex::new(separator) {
Some(crash_if_err!(1, regex::bytes::Regex::new(separator))) Ok(p) => Some(p),
Err(e) => return Err(TacError::InvalidRegex(e).into()),
}
} else { } else {
None None
}; };
@ -234,8 +240,8 @@ fn tac(filenames: Vec<&str>, before: bool, regex: bool, separator: &str) -> i32
} else { } else {
let mut buf1 = Vec::new(); let mut buf1 = Vec::new();
if let Err(e) = stdin().read_to_end(&mut buf1) { if let Err(e) = stdin().read_to_end(&mut buf1) {
show_error!("failed to read from stdin: {}", e); let e: Box<dyn UError> = TacError::ReadError("stdin".to_string(), e).into();
exit_code = 1; show!(e);
continue; continue;
} }
buf = buf1; buf = buf1;
@ -243,16 +249,15 @@ fn tac(filenames: Vec<&str>, before: bool, regex: bool, separator: &str) -> i32
} }
} else { } else {
let path = Path::new(filename); let path = Path::new(filename);
if path.is_dir() || path.metadata().is_err() { if path.is_dir() {
if path.is_dir() { let e: Box<dyn UError> = TacError::InvalidArgument(String::from(filename)).into();
show_error!("{}: read error: Invalid argument", filename.maybe_quote()); show!(e);
} else { continue;
show_error!( }
"failed to open {} for reading: No such file or directory",
filename.quote() if path.metadata().is_err() {
); let e: Box<dyn UError> = TacError::FileNotFound(String::from(filename)).into();
} show!(e);
exit_code = 1;
continue; continue;
} }
@ -266,22 +271,28 @@ fn tac(filenames: Vec<&str>, before: bool, regex: bool, separator: &str) -> i32
&buf &buf
} }
Err(e) => { Err(e) => {
show_error!("failed to read {}: {}", filename.quote(), e); let s = format!("{}", filename.quote());
exit_code = 1; let e: Box<dyn UError> = TacError::ReadError(s.to_string(), e).into();
show!(e);
continue; continue;
} }
} }
} }
}; };
if let Some(pattern) = &pattern { // Select the appropriate `tac` algorithm based on whether the
buffer_tac_regex(data, pattern, before) // separator is given as a regular expression or a fixed string.
} else { let result = match maybe_pattern {
buffer_tac(data, before, separator) Some(ref pattern) => buffer_tac_regex(data, pattern, before),
None => buffer_tac(data, before, separator),
};
// If there is any error in writing the output, terminate immediately.
if let Err(e) = result {
return Err(TacError::WriteError(e).into());
} }
.unwrap_or_else(|e| crash!(1, "failed to write to stdout: {}", e));
} }
exit_code Ok(())
} }
fn try_mmap_stdin() -> Option<Mmap> { fn try_mmap_stdin() -> Option<Mmap> {