1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-30 12:37:49 +00:00

Merge branch 'master' into split-uresult

This commit is contained in:
Sylvestre Ledru 2022-01-05 13:51:48 +01:00 committed by GitHub
commit 64effa5e78
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
23 changed files with 208 additions and 432 deletions

View file

@ -844,13 +844,13 @@ jobs:
## Generate coverage data
COVERAGE_REPORT_DIR="target/debug"
COVERAGE_REPORT_FILE="${COVERAGE_REPORT_DIR}/lcov.info"
# GRCOV_IGNORE_OPTION='--ignore build.rs --ignore "/*" --ignore "[a-zA-Z]:/*"' ## `grcov` ignores these params when passed as an environment variable (why?)
# GRCOV_IGNORE_OPTION='--ignore build.rs --ignore "vendor/*" --ignore "/*" --ignore "[a-zA-Z]:/*"' ## `grcov` ignores these params when passed as an environment variable (why?)
# GRCOV_EXCLUDE_OPTION='--excl-br-line "^\s*((debug_)?assert(_eq|_ne)?!|#\[derive\()"' ## `grcov` ignores these params when passed as an environment variable (why?)
mkdir -p "${COVERAGE_REPORT_DIR}"
# display coverage files
grcov . --output-type files --ignore build.rs --ignore "/*" --ignore "[a-zA-Z]:/*" --excl-br-line "^\s*((debug_)?assert(_eq|_ne)?!|#\[derive\()" | sort --unique
grcov . --output-type files --ignore build.rs --ignore "vendor/*" --ignore "/*" --ignore "[a-zA-Z]:/*" --excl-br-line "^\s*((debug_)?assert(_eq|_ne)?!|#\[derive\()" | sort --unique
# generate coverage report
grcov . --output-type lcov --output-path "${COVERAGE_REPORT_FILE}" --branch --ignore build.rs --ignore "/*" --ignore "[a-zA-Z]:/*" --excl-br-line "^\s*((debug_)?assert(_eq|_ne)?!|#\[derive\()"
grcov . --output-type lcov --output-path "${COVERAGE_REPORT_FILE}" --branch --ignore build.rs --ignore "vendor/*" --ignore "/*" --ignore "[a-zA-Z]:/*" --excl-br-line "^\s*((debug_)?assert(_eq|_ne)?!|#\[derive\()"
echo ::set-output name=report::${COVERAGE_REPORT_FILE}
- name: Upload coverage results (to Codecov.io)
uses: codecov/codecov-action@v1

1
Cargo.lock generated
View file

@ -3238,7 +3238,6 @@ dependencies = [
"data-encoding-macro",
"dns-lookup",
"dunce",
"getopts",
"lazy_static",
"libc",
"nix 0.23.1",

View file

@ -2,7 +2,8 @@
#![allow(clippy::upper_case_acronyms)]
use uucore::{display::Quotable, show_error, show_usage_error, show_warning};
use uucore::error::{UResult, USimpleError, UUsageError};
use uucore::{display::Quotable, show_error, show_warning};
use clap::{App, Arg};
use selinux::{OpaqueSecurityContext, SecurityContext};
@ -60,7 +61,8 @@ fn get_usage() -> String {
)
}
pub fn uumain(args: impl uucore::Args) -> i32 {
#[uucore_procs::gen_uumain]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let usage = get_usage();
let config = uu_app().usage(usage.as_ref());
@ -72,14 +74,13 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
match r.kind {
clap::ErrorKind::HelpDisplayed | clap::ErrorKind::VersionDisplayed => {
println!("{}", r);
return libc::EXIT_SUCCESS;
return Ok(());
}
_ => {}
}
}
show_usage_error!("{}.\n", r);
return libc::EXIT_FAILURE;
return Err(UUsageError::new(libc::EXIT_FAILURE, format!("{}.\n", r)));
}
};
@ -98,8 +99,10 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
match result {
Err(r) => {
show_error!("{}.", report_full_error(&r));
return libc::EXIT_FAILURE;
return Err(USimpleError::new(
libc::EXIT_FAILURE,
format!("{}.", report_full_error(&r)),
));
}
Ok(file_context) => SELinuxSecurityContext::File(file_context),
@ -111,14 +114,18 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
Ok(context) => context,
Err(_r) => {
show_error!("Invalid security context {}.", context.quote());
return libc::EXIT_FAILURE;
return Err(USimpleError::new(
libc::EXIT_FAILURE,
format!("Invalid security context {}.", context.quote()),
));
}
};
if SecurityContext::from_c_str(&c_context, false).check() == Some(false) {
show_error!("Invalid security context {}.", context.quote());
return libc::EXIT_FAILURE;
return Err(USimpleError::new(
libc::EXIT_FAILURE,
format!("Invalid security context {}.", context.quote()),
));
}
SELinuxSecurityContext::String(Some(c_context))
@ -132,8 +139,10 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
Ok(r) => Some(r),
Err(r) => {
show_error!("{}.", report_full_error(&r));
return libc::EXIT_FAILURE;
return Err(USimpleError::new(
libc::EXIT_FAILURE,
format!("{}.", report_full_error(&r)),
));
}
}
} else {
@ -142,13 +151,13 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
let results = process_files(&options, &context, root_dev_ino);
if results.is_empty() {
return libc::EXIT_SUCCESS;
return Ok(());
}
for result in &results {
show_error!("{}.", report_full_error(result));
}
libc::EXIT_FAILURE
Err(libc::EXIT_FAILURE.into())
}
pub fn uu_app() -> App<'static, 'static> {

View file

@ -466,12 +466,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
delim = "=";
}
if delim.chars().count() > 1 {
Err(msg_opt_invalid_should_be!(
"empty or 1 character long",
"a value 2 characters or longer",
"--delimiter",
"-d"
))
Err("invalid input: The '--delimiter' ('-d') option expects empty or 1 character long, but was provided a value 2 characters or longer".into())
} else {
let delim = if delim.is_empty() {
"\0".to_owned()
@ -503,13 +498,9 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
})
}
(ref b, ref c, ref f) if b.is_some() || c.is_some() || f.is_some() => Err(
msg_expects_no_more_than_one_of!("--fields (-f)", "--chars (-c)", "--bytes (-b)"),
"invalid usage: expects no more than one of --fields (-f), --chars (-c) or --bytes (-b)".into()
),
_ => Err(msg_expects_one_of!(
"--fields (-f)",
"--chars (-c)",
"--bytes (-b)"
)),
_ => Err("invalid usage: expects one of --fields (-f), --chars (-c) or --bytes (-b)".into()),
};
let mode_parse = match mode_parse {
@ -518,20 +509,12 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
Mode::Bytes(_, _) | Mode::Characters(_, _)
if matches.is_present(options::DELIMITER) =>
{
Err(msg_opt_only_usable_if!(
"printing a sequence of fields",
"--delimiter",
"-d"
))
Err("invalid input: The '--delimiter' ('-d') option only usable if printing a sequence of fields".into())
}
Mode::Bytes(_, _) | Mode::Characters(_, _)
if matches.is_present(options::ONLY_DELIMITED) =>
{
Err(msg_opt_only_usable_if!(
"printing a sequence of fields",
"--only-delimited",
"-s"
))
Err("invalid input: The '--only-delimited' ('-s') option only usable if printing a sequence of fields".into())
}
_ => Ok(mode),
},

View file

@ -18,7 +18,7 @@ use std::env;
use std::fs;
#[cfg(not(windows))]
use std::fs::Metadata;
use std::io::{stderr, ErrorKind, Result, Write};
use std::io::{ErrorKind, Result};
use std::iter;
#[cfg(not(windows))]
use std::os::unix::fs::MetadataExt;
@ -292,8 +292,7 @@ fn du(
let read = match fs::read_dir(&my_stat.path) {
Ok(read) => read,
Err(e) => {
safe_writeln!(
stderr(),
eprintln!(
"{}: cannot read directory {}: {}",
options.util_name,
my_stat.path.quote(),

View file

@ -413,7 +413,7 @@ impl<'a> State<'a> {
// This is fatal if the check is enabled.
if input.check_order == CheckOrder::Enabled {
exit!(1);
std::process::exit(1);
}
self.has_failed = true;

View file

@ -220,16 +220,32 @@ fn format_string(
options: &NumfmtOptions,
implicit_padding: Option<isize>,
) -> Result<String> {
// strip the (optional) suffix before applying any transformation
let source_without_suffix = match &options.suffix {
Some(suffix) => source.strip_suffix(suffix).unwrap_or(source),
None => source,
};
let number = transform_to(
transform_from(source, &options.transform.from)?,
transform_from(source_without_suffix, &options.transform.from)?,
&options.transform.to,
options.round,
)?;
// bring back the suffix before applying padding
let number_with_suffix = match &options.suffix {
Some(suffix) => format!("{}{}", number, suffix),
None => number,
};
Ok(match implicit_padding.unwrap_or(options.padding) {
0 => number,
p if p > 0 => format!("{:>padding$}", number, padding = p as usize),
p => format!("{:<padding$}", number, padding = p.abs() as usize),
0 => number_with_suffix,
p if p > 0 => format!("{:>padding$}", number_with_suffix, padding = p as usize),
p => format!(
"{:<padding$}",
number_with_suffix,
padding = p.abs() as usize
),
})
}

View file

@ -142,6 +142,8 @@ fn parse_options(args: &ArgMatches) -> Result<NumfmtOptions> {
_ => unreachable!("Should be restricted by clap"),
};
let suffix = args.value_of(options::SUFFIX).map(|s| s.to_owned());
Ok(NumfmtOptions {
transform,
padding,
@ -149,6 +151,7 @@ fn parse_options(args: &ArgMatches) -> Result<NumfmtOptions> {
fields,
delimiter,
round,
suffix,
})
}
@ -242,5 +245,14 @@ pub fn uu_app() -> App<'static, 'static> {
.default_value("from-zero")
.possible_values(&["up", "down", "from-zero", "towards-zero", "nearest"]),
)
.arg(
Arg::with_name(options::SUFFIX)
.long(options::SUFFIX)
.help(
"print SUFFIX after each formatted number, and accept \
inputs optionally ending with SUFFIX",
)
.value_name("SUFFIX"),
)
.arg(Arg::with_name(options::NUMBER).hidden(true).multiple(true))
}

View file

@ -11,6 +11,7 @@ pub const HEADER_DEFAULT: &str = "1";
pub const NUMBER: &str = "NUMBER";
pub const PADDING: &str = "padding";
pub const ROUND: &str = "round";
pub const SUFFIX: &str = "suffix";
pub const TO: &str = "to";
pub const TO_DEFAULT: &str = "none";
@ -26,6 +27,7 @@ pub struct NumfmtOptions {
pub fields: Vec<Range>,
pub delimiter: Option<String>,
pub round: RoundMethod,
pub suffix: Option<String>,
}
#[derive(Clone, Copy)]

View file

@ -410,7 +410,7 @@ fn get_size(size_str_opt: Option<String>) -> Option<u64> {
util_name(),
size_str_opt.unwrap().maybe_quote()
);
exit!(1);
std::process::exit(1);
}
};

View file

@ -20,17 +20,23 @@ mod platform {
}
fn main() {
let manifest_dir = env::var("CARGO_MANIFEST_DIR").expect("Could not find manifest dir");
let profile = env::var("PROFILE").expect("Could not determine profile");
let out_dir = env::var("OUT_DIR").unwrap();
let libstdbuf = format!(
"{}/../../../{}/{}/deps/liblibstdbuf{}",
manifest_dir,
env::var("CARGO_TARGET_DIR").unwrap_or_else(|_| "target".to_string()),
profile,
platform::DYLIB_EXT
);
let mut target_dir = Path::new(&out_dir);
// Depending on how this is util is built, the directory structure. This seems to work for now.
// Here are three cases to test when changing this:
// - cargo run
// - cross run
// - cargo install --git
let mut name = target_dir.file_name().unwrap().to_string_lossy();
while name != "target" && !name.starts_with("cargo-install") {
target_dir = target_dir.parent().unwrap();
name = target_dir.file_name().unwrap().to_string_lossy();
}
let mut libstdbuf = target_dir.to_path_buf();
libstdbuf.push(env::var("PROFILE").unwrap());
libstdbuf.push("deps");
libstdbuf.push(format!("liblibstdbuf{}", platform::DYLIB_EXT));
fs::copy(libstdbuf, Path::new(&out_dir).join("libstdbuf.so")).unwrap();
}

View file

@ -12,9 +12,10 @@ extern crate uucore;
use clap::{crate_version, App, Arg};
use std::fs::File;
use std::io::{stdin, Read, Result};
use std::io::{stdin, Read};
use std::path::Path;
use uucore::display::Quotable;
use uucore::error::{FromIo, UResult, USimpleError};
use uucore::InvalidEncodingHandling;
static NAME: &str = "sum";
@ -65,26 +66,25 @@ fn sysv_sum(mut reader: Box<dyn Read>) -> (usize, u16) {
(blocks_read, ret as u16)
}
fn open(name: &str) -> Result<Box<dyn Read>> {
fn open(name: &str) -> UResult<Box<dyn Read>> {
match name {
"-" => Ok(Box::new(stdin()) as Box<dyn Read>),
_ => {
let path = &Path::new(name);
if path.is_dir() {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
"Is a directory",
return Err(USimpleError::new(
2,
format!("{}: Is a directory", name.maybe_quote()),
));
};
// Silent the warning as we want to the error message
#[allow(clippy::question_mark)]
if path.metadata().is_err() {
return Err(std::io::Error::new(
std::io::ErrorKind::NotFound,
"No such file or directory",
return Err(USimpleError::new(
2,
format!("{}: No such file or directory", name.maybe_quote()),
));
};
let f = File::open(path)?;
let f = File::open(path).map_err_context(String::new)?;
Ok(Box::new(f) as Box<dyn Read>)
}
}
@ -96,7 +96,8 @@ mod options {
pub static SYSTEM_V_COMPATIBLE: &str = "sysv";
}
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();
@ -116,13 +117,11 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
files.len() > 1
};
let mut exit_code = 0;
for file in &files {
let reader = match open(file) {
Ok(f) => f,
Err(error) => {
show_error!("{}: {}", file.maybe_quote(), error);
exit_code = 2;
show!(error);
continue;
}
};
@ -138,8 +137,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
println!("{} {}", sum, blocks);
}
}
exit_code
Ok(())
}
pub fn uu_app() -> App<'static, 'static> {

View file

@ -9,14 +9,10 @@
extern crate libc;
#[macro_use]
extern crate uucore;
use clap::{crate_version, App, Arg};
use std::path::Path;
use uucore::display::Quotable;
static EXIT_ERR: i32 = 1;
use uucore::error::{UResult, USimpleError};
static ABOUT: &str = "Synchronize cached writes to persistent storage";
pub mod options {
@ -72,6 +68,7 @@ mod platform {
use std::mem;
use std::os::windows::prelude::*;
use std::path::Path;
use uucore::crash;
use uucore::wide::{FromWide, ToWide};
unsafe fn flush_volume(name: &str) {
@ -164,7 +161,8 @@ fn usage() -> String {
format!("{0} [OPTION]... FILE...", 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);
@ -176,11 +174,10 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
for f in &files {
if !Path::new(&f).exists() {
crash!(
EXIT_ERR,
"cannot stat {}: No such file or directory",
f.quote()
);
return Err(USimpleError::new(
1,
format!("cannot stat {}: No such file or directory", f.quote()),
));
}
}
@ -194,7 +191,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
} else {
sync();
}
0
Ok(())
}
pub fn uu_app() -> App<'static, 'static> {

View file

@ -14,6 +14,7 @@ use std::fs::OpenOptions;
use std::io::{copy, sink, stdin, stdout, Error, ErrorKind, Read, Result, Write};
use std::path::PathBuf;
use uucore::display::Quotable;
use uucore::error::UResult;
#[cfg(unix)]
use uucore::libc;
@ -37,7 +38,8 @@ fn usage() -> String {
format!("{0} [OPTION]... [FILE]...", 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);
@ -52,8 +54,8 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
};
match tee(options) {
Ok(_) => 0,
Err(_) => 1,
Ok(_) => Ok(()),
Err(_) => Err(1.into()),
}
}

View file

@ -13,7 +13,8 @@ mod parser;
use clap::{crate_version, App, AppSettings};
use parser::{parse, Operator, Symbol, UnaryOperator};
use std::ffi::{OsStr, OsString};
use uucore::{display::Quotable, show_error};
use uucore::display::Quotable;
use uucore::error::{UResult, USimpleError};
const USAGE: &str = "test EXPRESSION
or: test
@ -91,7 +92,8 @@ pub fn uu_app() -> App<'static, 'static> {
.setting(AppSettings::DisableVersion)
}
pub fn uumain(mut args: impl uucore::Args) -> i32 {
#[uucore_procs::gen_uumain]
pub fn uumain(mut args: impl uucore::Args) -> UResult<()> {
let program = args.next().unwrap_or_else(|| OsString::from("test"));
let binary_name = uucore::util_name();
let mut args: Vec<_> = args.collect();
@ -109,13 +111,12 @@ pub fn uumain(mut args: impl uucore::Args) -> i32 {
.setting(AppSettings::NeedsLongHelp)
.setting(AppSettings::NeedsLongVersion)
.get_matches_from(std::iter::once(program).chain(args.into_iter()));
return 0;
return Ok(());
}
// If invoked via name '[', matching ']' must be in the last arg
let last = args.pop();
if last.as_deref() != Some(OsStr::new("]")) {
show_error!("missing ']'");
return 2;
return Err(USimpleError::new(2, "missing ']'"));
}
}
@ -124,15 +125,12 @@ pub fn uumain(mut args: impl uucore::Args) -> i32 {
match result {
Ok(result) => {
if result {
0
Ok(())
} else {
1
Err(1.into())
}
}
Err(e) => {
show_error!("{}", e);
2
}
Err(e) => Err(USimpleError::new(2, e)),
}
}

View file

@ -12,6 +12,7 @@
use clap::{crate_version, App, Arg};
use std::ffi::CStr;
use std::io::Write;
use uucore::error::{UResult, UUsageError};
use uucore::InvalidEncodingHandling;
static ABOUT: &str = "Print the file name of the terminal connected to standard input.";
@ -24,21 +25,17 @@ fn usage() -> String {
format!("{0} [OPTION]...", 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 args = args
.collect_str(InvalidEncodingHandling::ConvertLossy)
.accept_any();
let matches = uu_app().usage(&usage[..]).get_matches_from_safe(args);
let matches = match matches {
Ok(m) => m,
Err(e) => {
eprint!("{}", e);
return 2;
}
};
let matches = uu_app()
.usage(&usage[..])
.get_matches_from_safe(args)
.map_err(|e| UUsageError::new(2, format!("{}", e)))?;
let silent = matches.is_present(options::SILENT);
@ -68,9 +65,9 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
}
if atty::is(atty::Stream::Stdin) {
libc::EXIT_SUCCESS
Ok(())
} else {
libc::EXIT_FAILURE
Err(libc::EXIT_FAILURE.into())
}
}

View file

@ -17,6 +17,7 @@ use std::io::{stdin, stdout, BufRead, BufReader, BufWriter, Read, Stdout, Write}
use std::str::from_utf8;
use unicode_width::UnicodeWidthChar;
use uucore::display::Quotable;
use uucore::error::{FromIo, UResult};
use uucore::InvalidEncodingHandling;
static NAME: &str = "unexpand";
@ -90,16 +91,15 @@ impl Options {
}
}
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::Ignore)
.accept_any();
let matches = uu_app().get_matches_from(args);
unexpand(Options::new(matches));
0
unexpand(Options::new(matches)).map_err_context(String::new)
}
pub fn uu_app() -> App<'static, 'static> {
@ -242,7 +242,7 @@ fn next_char_info(uflag: bool, buf: &[u8], byte: usize) -> (CharType, usize, usi
(ctype, cwidth, nbytes)
}
fn unexpand(options: Options) {
fn unexpand(options: Options) -> std::io::Result<()> {
let mut output = BufWriter::new(stdout());
let ts = &options.tabstops[..];
let mut buf = Vec::new();
@ -273,7 +273,7 @@ fn unexpand(options: Options) {
init,
true,
);
crash_if_err!(1, output.write_all(&buf[byte..]));
output.write_all(&buf[byte..])?;
scol = col;
break;
}
@ -293,7 +293,7 @@ fn unexpand(options: Options) {
};
if !tabs_buffered {
crash_if_err!(1, output.write_all(&buf[byte..byte + nbytes]));
output.write_all(&buf[byte..byte + nbytes])?;
scol = col; // now printed up to this column
}
}
@ -318,7 +318,7 @@ fn unexpand(options: Options) {
} else {
0
};
crash_if_err!(1, output.write_all(&buf[byte..byte + nbytes]));
output.write_all(&buf[byte..byte + nbytes])?;
scol = col; // we've now printed up to this column
}
}
@ -337,9 +337,9 @@ fn unexpand(options: Options) {
init,
true,
);
crash_if_err!(1, output.flush());
output.flush()?;
buf.truncate(0); // clear out the buffer
}
}
crash_if_err!(1, output.flush())
output.flush()
}

View file

@ -19,7 +19,6 @@ path="src/lib/lib.rs"
clap = "2.33.3"
dns-lookup = { version="1.0.5", optional=true }
dunce = "1.0.0"
getopts = "<= 0.2.21"
wild = "2.0"
# * optional
thiserror = { version="1.0", optional=true }

View file

@ -18,7 +18,6 @@ mod parser; // string parsing modules
// * cross-platform modules
pub use crate::mods::backup_control;
pub use crate::mods::coreopts;
pub use crate::mods::display;
pub use crate::mods::error;
pub use crate::mods::os;

View file

@ -26,9 +26,7 @@
//! - From custom messages: [`show_error!`], [`show_usage_error!`]
//! - Print warnings: [`show_warning!`]
//! - Terminate util execution
//! - Terminate regularly: [`exit!`], [`return_if_err!`]
//! - Crash program: [`crash!`], [`crash_if_err!`], [`safe_unwrap!`]
//! - Unwrapping result types: [`safe_unwrap!`]
//! - Crash program: [`crash!`], [`crash_if_err!`]
// spell-checker:ignore sourcepath targetpath
@ -223,22 +221,10 @@ macro_rules! show_usage_error(
})
);
//====
/// Calls [`std::process::exit`] with the provided exit code.
///
/// Why not call exit directly?
#[macro_export]
macro_rules! exit(
($exit_code:expr) => ({
::std::process::exit($exit_code)
})
);
/// Display an error and [`exit!`]
///
/// Displays the provided error message using [`show_error!`], then invokes
/// [`exit!`] with the provided exit code.
/// [`std::process::exit`] with the provided exit code.
///
/// # Examples
///
@ -255,7 +241,7 @@ macro_rules! exit(
macro_rules! crash(
($exit_code:expr, $($args:tt)+) => ({
$crate::show_error!($($args)+);
$crate::exit!($exit_code)
std::process::exit($exit_code);
})
);
@ -288,150 +274,3 @@ macro_rules! crash_if_err(
}
)
);
/// Unwrap some Result, crashing instead of panicking.
///
/// Drop this in favor of `crash_if_err!`
#[macro_export]
macro_rules! safe_unwrap(
($exp:expr) => (
match $exp {
Ok(m) => m,
Err(f) => $crate::crash!(1, "{}", f.to_string())
}
)
);
//====
/// Unwraps the Result. Instead of panicking, it shows the error and then
/// returns from the function with the provided exit code.
/// Assumes the current function returns an i32 value.
///
/// Replace with `crash_if_err`?
#[macro_export]
macro_rules! return_if_err(
($exit_code:expr, $exp:expr) => (
match $exp {
Ok(m) => m,
Err(f) => {
$crate::show_error!("{}", f);
return $exit_code;
}
}
)
);
//====
/// This is used exclusively by du...
#[macro_export]
macro_rules! safe_writeln(
($fd:expr, $($args:tt)+) => (
match writeln!($fd, $($args)+) {
Ok(_) => {}
Err(f) => panic!("{}", f)
}
)
);
//-- message templates
//-- message templates : (join utility sub-macros)
// used only by "cut"
#[macro_export]
macro_rules! snippet_list_join_oxford_comma {
($conjunction:expr, $valOne:expr, $valTwo:expr) => (
format!("{}, {} {}", $valOne, $conjunction, $valTwo)
);
($conjunction:expr, $valOne:expr, $valTwo:expr $(, $remaining_values:expr)*) => (
format!("{}, {}", $valOne, $crate::snippet_list_join_oxford_comma!($conjunction, $valTwo $(, $remaining_values)*))
);
}
// used only by "cut"
#[macro_export]
macro_rules! snippet_list_join {
($conjunction:expr, $valOne:expr, $valTwo:expr) => (
format!("{} {} {}", $valOne, $conjunction, $valTwo)
);
($conjunction:expr, $valOne:expr, $valTwo:expr $(, $remaining_values:expr)*) => (
format!("{}, {}", $valOne, $crate::snippet_list_join_oxford_comma!($conjunction, $valTwo $(, $remaining_values)*))
);
}
//-- message templates : invalid input
#[macro_export]
macro_rules! msg_invalid_input {
($reason: expr) => {
format!("invalid input: {}", $reason)
};
}
// -- message templates : invalid input : flag
#[macro_export]
macro_rules! msg_invalid_opt_use {
($about:expr, $flag:expr) => {
$crate::msg_invalid_input!(format!("The '{}' option {}", $flag, $about))
};
($about:expr, $long_flag:expr, $short_flag:expr) => {
$crate::msg_invalid_input!(format!(
"The '{}' ('{}') option {}",
$long_flag, $short_flag, $about
))
};
}
// Only used by "cut"
#[macro_export]
macro_rules! msg_opt_only_usable_if {
($clause:expr, $flag:expr) => {
$crate::msg_invalid_opt_use!(format!("only usable if {}", $clause), $flag)
};
($clause:expr, $long_flag:expr, $short_flag:expr) => {
$crate::msg_invalid_opt_use!(
format!("only usable if {}", $clause),
$long_flag,
$short_flag
)
};
}
// Used only by "cut"
#[macro_export]
macro_rules! msg_opt_invalid_should_be {
($expects:expr, $received:expr, $flag:expr) => {
$crate::msg_invalid_opt_use!(
format!("expects {}, but was provided {}", $expects, $received),
$flag
)
};
($expects:expr, $received:expr, $long_flag:expr, $short_flag:expr) => {
$crate::msg_invalid_opt_use!(
format!("expects {}, but was provided {}", $expects, $received),
$long_flag,
$short_flag
)
};
}
// -- message templates : invalid input : input combinations
// UNUSED!
#[macro_export]
macro_rules! msg_expects_one_of {
($valOne:expr $(, $remaining_values:expr)*) => (
$crate::msg_invalid_input!(format!("expects one of {}", $crate::snippet_list_join!("or", $valOne $(, $remaining_values)*)))
);
}
// Used only by "cut"
#[macro_export]
macro_rules! msg_expects_no_more_than_one_of {
($valOne:expr $(, $remaining_values:expr)*) => (
$crate::msg_invalid_input!(format!("expects no more than one of {}", $crate::snippet_list_join!("or", $valOne $(, $remaining_values)*))) ;
);
}

View file

@ -1,7 +1,6 @@
// mods ~ cross-platforms modules (core/bundler file)
pub mod backup_control;
pub mod coreopts;
pub mod display;
pub mod error;
pub mod os;

View file

@ -1,141 +0,0 @@
pub struct HelpText<'a> {
pub name: &'a str,
pub version: &'a str,
pub syntax: &'a str,
pub summary: &'a str,
pub long_help: &'a str,
pub display_usage: bool,
}
pub struct CoreOptions<'a> {
options: getopts::Options,
help_text: HelpText<'a>,
}
impl<'a> CoreOptions<'a> {
pub fn new(help_text: HelpText<'a>) -> Self {
let mut ret = CoreOptions {
options: getopts::Options::new(),
help_text,
};
ret.options
.optflag("", "help", "print usage information")
.optflag("", "version", "print name and version number");
ret
}
pub fn optflagopt(
&mut self,
short_name: &str,
long_name: &str,
desc: &str,
hint: &str,
) -> &mut CoreOptions<'a> {
self.options.optflagopt(short_name, long_name, desc, hint);
self
}
pub fn optflag(
&mut self,
short_name: &str,
long_name: &str,
desc: &str,
) -> &mut CoreOptions<'a> {
self.options.optflag(short_name, long_name, desc);
self
}
pub fn optflagmulti(
&mut self,
short_name: &str,
long_name: &str,
desc: &str,
) -> &mut CoreOptions<'a> {
self.options.optflagmulti(short_name, long_name, desc);
self
}
pub fn optopt(
&mut self,
short_name: &str,
long_name: &str,
desc: &str,
hint: &str,
) -> &mut CoreOptions<'a> {
self.options.optopt(short_name, long_name, desc, hint);
self
}
pub fn optmulti(
&mut self,
short_name: &str,
long_name: &str,
desc: &str,
hint: &str,
) -> &mut CoreOptions<'a> {
self.options.optmulti(short_name, long_name, desc, hint);
self
}
pub fn usage(&self, summary: &str) -> String {
self.options.usage(summary)
}
pub fn parse(&mut self, args: Vec<String>) -> getopts::Matches {
let matches = match self.options.parse(&args[1..]) {
Ok(m) => Some(m),
Err(f) => {
eprint!("{}: error: ", self.help_text.name);
eprintln!("{}", f);
::std::process::exit(1);
}
}
.unwrap();
if matches.opt_present("help") {
let usage_str = if self.help_text.display_usage {
format!(
"\n {}\n\n Reference\n",
self.options.usage(self.help_text.summary)
)
.replace("Options:", " Options:")
} else {
String::new()
};
print!(
"
{0} {1}
{0} {2}
{3}{4}
",
self.help_text.name,
self.help_text.version,
self.help_text.syntax,
usage_str,
self.help_text.long_help
);
crate::exit!(0);
} else if matches.opt_present("version") {
println!("{} {}", self.help_text.name, self.help_text.version);
crate::exit!(0);
}
matches
}
}
#[macro_export]
macro_rules! app {
($syntax: expr, $summary: expr, $long_help: expr) => {
uucore::coreopts::CoreOptions::new(uucore::coreopts::HelpText {
name: uucore::util_name(),
version: env!("CARGO_PKG_VERSION"),
syntax: $syntax,
summary: $summary,
long_help: $long_help,
display_usage: true,
})
};
($syntax: expr, $summary: expr, $long_help: expr, $display_usage: expr) => {
uucore::coreopts::CoreOptions::new(uucore::coreopts::HelpText {
name: uucore::util_name(),
version: env!("CARGO_PKG_VERSION"),
syntax: $syntax,
summary: $summary,
long_help: $long_help,
display_usage: $display_usage,
})
};
}

View file

@ -505,3 +505,66 @@ fn test_round() {
.stdout_only(exp.join("\n") + "\n");
}
}
#[test]
fn test_suffix_is_added_if_not_supplied() {
new_ucmd!()
.args(&["--suffix=TEST"])
.pipe_in("1000")
.succeeds()
.stdout_only("1000TEST\n");
}
#[test]
fn test_suffix_is_preserved() {
new_ucmd!()
.args(&["--suffix=TEST"])
.pipe_in("1000TEST")
.succeeds()
.stdout_only("1000TEST\n");
}
#[test]
fn test_suffix_is_only_applied_to_selected_field() {
new_ucmd!()
.args(&["--suffix=TEST", "--field=2"])
.pipe_in("1000 2000 3000")
.succeeds()
.stdout_only("1000 2000TEST 3000\n");
}
#[test]
fn test_transform_with_suffix_on_input() {
new_ucmd!()
.args(&["--suffix=b", "--to=si"])
.pipe_in("2000b")
.succeeds()
.stdout_only("2.0Kb\n");
}
#[test]
fn test_transform_without_suffix_on_input() {
new_ucmd!()
.args(&["--suffix=b", "--to=si"])
.pipe_in("2000")
.succeeds()
.stdout_only("2.0Kb\n");
}
#[test]
fn test_transform_with_suffix_and_delimiter() {
new_ucmd!()
.args(&["--suffix=b", "--to=si", "-d=|"])
.pipe_in("1000b|2000|3000")
.succeeds()
.stdout_only("1.0Kb|2000|3000\n");
}
#[test]
fn test_suffix_with_padding() {
new_ucmd!()
.args(&["--suffix=pad", "--padding=12"])
.pipe_in("1000 2000 3000")
.succeeds()
.stdout_only(" 1000pad 2000 3000\n");
}