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

Merge pull request #2783 from jfinkels/tac-uresult

tac: return UResult from uumain() function
This commit is contained in:
Sylvestre Ledru 2021-12-24 10:36:12 +01:00 committed by GitHub
commit eb87ddbaf7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
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.
// spell-checker:ignore (ToDO) sbytes slen dlen memmem memmap Mmap mmap SIGBUS
#[macro_use]
extern crate uucore;
mod error;
use clap::{crate_version, App, Arg};
use memchr::memmem;
@ -19,8 +17,13 @@ use std::{
path::Path,
};
use uucore::display::Quotable;
use uucore::error::UError;
use uucore::error::UResult;
use uucore::show;
use uucore::InvalidEncodingHandling;
use crate::error::TacError;
static NAME: &str = "tac";
static USAGE: &str = "[OPTION]... [FILE]...";
static SUMMARY: &str = "Write each file to standard output, last line first.";
@ -32,7 +35,8 @@ mod options {
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
.collect_str(InvalidEncodingHandling::ConvertLossy)
.accept_any();
@ -214,11 +218,13 @@ fn buffer_tac(data: &[u8], before: bool, separator: &str) -> std::io::Result<()>
Ok(())
}
fn tac(filenames: Vec<&str>, before: bool, regex: bool, separator: &str) -> i32 {
let mut exit_code = 0;
let pattern = if regex {
Some(crash_if_err!(1, regex::bytes::Regex::new(separator)))
fn tac(filenames: Vec<&str>, before: bool, regex: bool, separator: &str) -> UResult<()> {
// Compile the regular expression pattern if it is provided.
let maybe_pattern = if regex {
match regex::bytes::Regex::new(separator) {
Ok(p) => Some(p),
Err(e) => return Err(TacError::InvalidRegex(e).into()),
}
} else {
None
};
@ -234,8 +240,8 @@ fn tac(filenames: Vec<&str>, before: bool, regex: bool, separator: &str) -> i32
} else {
let mut buf1 = Vec::new();
if let Err(e) = stdin().read_to_end(&mut buf1) {
show_error!("failed to read from stdin: {}", e);
exit_code = 1;
let e: Box<dyn UError> = TacError::ReadError("stdin".to_string(), e).into();
show!(e);
continue;
}
buf = buf1;
@ -243,16 +249,15 @@ fn tac(filenames: Vec<&str>, before: bool, regex: bool, separator: &str) -> i32
}
} else {
let path = Path::new(filename);
if path.is_dir() || path.metadata().is_err() {
if path.is_dir() {
show_error!("{}: read error: Invalid argument", filename.maybe_quote());
} else {
show_error!(
"failed to open {} for reading: No such file or directory",
filename.quote()
);
let e: Box<dyn UError> = TacError::InvalidArgument(String::from(filename)).into();
show!(e);
continue;
}
exit_code = 1;
if path.metadata().is_err() {
let e: Box<dyn UError> = TacError::FileNotFound(String::from(filename)).into();
show!(e);
continue;
}
@ -266,22 +271,28 @@ fn tac(filenames: Vec<&str>, before: bool, regex: bool, separator: &str) -> i32
&buf
}
Err(e) => {
show_error!("failed to read {}: {}", filename.quote(), e);
exit_code = 1;
let s = format!("{}", filename.quote());
let e: Box<dyn UError> = TacError::ReadError(s.to_string(), e).into();
show!(e);
continue;
}
}
}
};
if let Some(pattern) = &pattern {
buffer_tac_regex(data, pattern, before)
} else {
buffer_tac(data, before, separator)
// Select the appropriate `tac` algorithm based on whether the
// separator is given as a regular expression or a fixed string.
let result = match maybe_pattern {
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> {