mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-28 03:27:44 +00:00
parent
02e9ffecdd
commit
52f2ab6898
4 changed files with 150 additions and 6 deletions
|
@ -226,12 +226,31 @@ fn format_string(
|
|||
})
|
||||
}
|
||||
|
||||
/// Format a line of text according to the selected options.
|
||||
///
|
||||
/// Given a line of text `s`, split the line into fields, transform and format
|
||||
/// any selected numeric fields, and print the result to stdout. Fields not
|
||||
/// selected for conversion are passed through unmodified.
|
||||
pub fn format_and_print(s: &str, options: &NumfmtOptions) -> Result<()> {
|
||||
fn format_and_print_delimited(s: &str, options: &NumfmtOptions) -> Result<()> {
|
||||
let delimiter = options.delimiter.as_ref().unwrap();
|
||||
|
||||
for (n, field) in (1..).zip(s.split(delimiter)) {
|
||||
let field_selected = uucore::ranges::contain(&options.fields, n);
|
||||
|
||||
// print delimiter before second and subsequent fields
|
||||
if n > 1 {
|
||||
print!("{}", delimiter);
|
||||
}
|
||||
|
||||
if field_selected {
|
||||
print!("{}", format_string(&field.trim_start(), options, None)?);
|
||||
} else {
|
||||
// print unselected field without conversion
|
||||
print!("{}", field);
|
||||
}
|
||||
}
|
||||
|
||||
println!();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn format_and_print_whitespace(s: &str, options: &NumfmtOptions) -> Result<()> {
|
||||
for (n, (prefix, field)) in (1..).zip(WhitespaceSplitter { s: Some(s) }) {
|
||||
let field_selected = uucore::ranges::contain(&options.fields, n);
|
||||
|
||||
|
@ -263,3 +282,15 @@ pub fn format_and_print(s: &str, options: &NumfmtOptions) -> Result<()> {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Format a line of text according to the selected options.
|
||||
///
|
||||
/// Given a line of text `s`, split the line into fields, transform and format
|
||||
/// any selected numeric fields, and print the result to stdout. Fields not
|
||||
/// selected for conversion are passed through unmodified.
|
||||
pub fn format_and_print(s: &str, options: &NumfmtOptions) -> Result<()> {
|
||||
match &options.delimiter {
|
||||
Some(_) => format_and_print_delimited(s, options),
|
||||
None => format_and_print_whitespace(s, options),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -128,11 +128,20 @@ fn parse_options(args: &ArgMatches) -> Result<NumfmtOptions> {
|
|||
None => unreachable!(),
|
||||
};
|
||||
|
||||
let delimiter = args.value_of(options::DELIMITER).map_or(Ok(None), |arg| {
|
||||
if arg.len() == 1 {
|
||||
Ok(Some(arg.to_string()))
|
||||
} else {
|
||||
Err("the delimiter must be a single character".to_string())
|
||||
}
|
||||
})?;
|
||||
|
||||
Ok(NumfmtOptions {
|
||||
transform,
|
||||
padding,
|
||||
header,
|
||||
fields,
|
||||
delimiter,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -145,6 +154,13 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
.usage(&usage[..])
|
||||
.after_help(LONG_HELP)
|
||||
.setting(AppSettings::AllowNegativeNumbers)
|
||||
.arg(
|
||||
Arg::with_name(options::DELIMITER)
|
||||
.short("d")
|
||||
.long(options::DELIMITER)
|
||||
.value_name("X")
|
||||
.help("use X instead of whitespace for field delimiter"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(options::FIELD)
|
||||
.long(options::FIELD)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use crate::units::Transform;
|
||||
use uucore::ranges::Range;
|
||||
|
||||
pub const DELIMITER: &str = "delimiter";
|
||||
pub const FIELD: &str = "field";
|
||||
pub const FIELD_DEFAULT: &str = "1";
|
||||
pub const FROM: &str = "from";
|
||||
|
@ -22,4 +23,5 @@ pub struct NumfmtOptions {
|
|||
pub padding: isize,
|
||||
pub header: usize,
|
||||
pub fields: Vec<Range>,
|
||||
pub delimiter: Option<String>,
|
||||
}
|
||||
|
|
|
@ -383,3 +383,98 @@ fn test_field_df_example() {
|
|||
.succeeds()
|
||||
.stdout_is_fixture("df_expected.txt");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_delimiter_must_not_be_empty() {
|
||||
new_ucmd!().args(&["-d"]).fails();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_delimiter_must_not_be_more_than_one_character() {
|
||||
new_ucmd!()
|
||||
.args(&["--delimiter", "sad"])
|
||||
.fails()
|
||||
.stderr_is("numfmt: the delimiter must be a single character");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_delimiter_only() {
|
||||
new_ucmd!()
|
||||
.args(&["-d", ","])
|
||||
.pipe_in("1234,56")
|
||||
.succeeds()
|
||||
.stdout_only("1234,56\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_line_is_field_with_no_delimiter() {
|
||||
new_ucmd!()
|
||||
.args(&["-d,", "--to=iec"])
|
||||
.pipe_in("123456")
|
||||
.succeeds()
|
||||
.stdout_only("121K\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_delimiter_to_si() {
|
||||
new_ucmd!()
|
||||
.args(&["-d=,", "--to=si"])
|
||||
.pipe_in("1234,56")
|
||||
.succeeds()
|
||||
.stdout_only("1.3K,56\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_delimiter_skips_leading_whitespace() {
|
||||
new_ucmd!()
|
||||
.args(&["-d=,", "--to=si"])
|
||||
.pipe_in(" \t 1234,56")
|
||||
.succeeds()
|
||||
.stdout_only("1.3K,56\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_delimiter_preserves_leading_whitespace_in_unselected_fields() {
|
||||
new_ucmd!()
|
||||
.args(&["-d=|", "--to=si"])
|
||||
.pipe_in(" 1000| 2000")
|
||||
.succeeds()
|
||||
.stdout_only("1.0K| 2000\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_delimiter_from_si() {
|
||||
new_ucmd!()
|
||||
.args(&["-d=,", "--from=si"])
|
||||
.pipe_in("1.2K,56")
|
||||
.succeeds()
|
||||
.stdout_only("1200,56\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_delimiter_overrides_whitespace_separator() {
|
||||
// GNU numfmt reports this as “invalid suffix”
|
||||
new_ucmd!()
|
||||
.args(&["-d,"])
|
||||
.pipe_in("1 234,56")
|
||||
.fails()
|
||||
.stderr_is("numfmt: invalid number: ‘1 234’\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_delimiter_with_padding() {
|
||||
new_ucmd!()
|
||||
.args(&["-d=|", "--to=si", "--padding=5"])
|
||||
.pipe_in("1000|2000")
|
||||
.succeeds()
|
||||
.stdout_only(" 1.0K|2000\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_delimiter_with_padding_and_fields() {
|
||||
new_ucmd!()
|
||||
.args(&["-d=|", "--to=si", "--padding=5", "--field=-"])
|
||||
.pipe_in("1000|2000")
|
||||
.succeeds()
|
||||
.stdout_only(" 1.0K| 2.0K\n");
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue