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:
commit
eb87ddbaf7
2 changed files with 100 additions and 29 deletions
60
src/uu/tac/src/error.rs
Normal file
60
src/uu/tac/src/error.rs
Normal 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),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
);
|
||||
}
|
||||
exit_code = 1;
|
||||
if path.is_dir() {
|
||||
let e: Box<dyn UError> = TacError::InvalidArgument(String::from(filename)).into();
|
||||
show!(e);
|
||||
continue;
|
||||
}
|
||||
|
||||
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> {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue