From ad713fd1d975dde1389642481fca8926d6467407 Mon Sep 17 00:00:00 2001 From: Terts Diepraam Date: Wed, 16 Nov 2022 20:48:21 +0100 Subject: [PATCH 1/8] stat: clean up some comments --- src/uu/stat/src/stat.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/uu/stat/src/stat.rs b/src/uu/stat/src/stat.rs index b7bea118e..decbd3822 100644 --- a/src/uu/stat/src/stat.rs +++ b/src/uu/stat/src/stat.rs @@ -44,13 +44,11 @@ pub const F_ZERO: u8 = 1 << 1; pub const F_LEFT: u8 = 1 << 2; pub const F_SPACE: u8 = 1 << 3; pub const F_SIGN: u8 = 1 << 4; -// unused at present pub const F_GROUP: u8 = 1 << 5; /// checks if the string is within the specified bound, /// if it gets out of bound, error out by printing sub-string from index `beg` to`end`, /// where `beg` & `end` is the beginning and end index of sub-string, respectively -/// fn check_bound(slice: &str, bound: usize, beg: usize, end: usize) -> UResult<()> { if end >= bound { return Err(USimpleError::new( @@ -63,7 +61,6 @@ fn check_bound(slice: &str, bound: usize, beg: usize, end: usize) -> UResult<()> /// pads the string with zeroes if supplied min is greater /// then the length of the string, else returns the original string -/// fn extend_digits(string: &str, min: usize) -> Cow<'_, str> { if min > string.len() { let mut pad = String::with_capacity(min); @@ -91,8 +88,7 @@ enum Padding { /// ``` /// currently only supports '0' & ' ' as the padding character /// because the format specification of print! does not support general -/// fill characters -/// +/// fill characters. fn pad_and_print(result: &str, left: bool, width: usize, padding: Padding) { match (left, padding) { (false, Padding::Zero) => print!("{result:0>width$}"), From 7e5b6400e3d3438146be2d87afc73b72c67fda49 Mon Sep 17 00:00:00 2001 From: Terts Diepraam Date: Wed, 16 Nov 2022 21:12:40 +0100 Subject: [PATCH 2/8] stat: use Flags struct instead of u8 bit manipulation --- src/uu/stat/src/stat.rs | 59 +++++++++++++++++++------------------- tests/by-util/test_stat.rs | 25 +++++++++++++--- 2 files changed, 51 insertions(+), 33 deletions(-) diff --git a/src/uu/stat/src/stat.rs b/src/uu/stat/src/stat.rs index decbd3822..600fdd304 100644 --- a/src/uu/stat/src/stat.rs +++ b/src/uu/stat/src/stat.rs @@ -39,12 +39,15 @@ pub mod options { static ARG_FILES: &str = "files"; -pub const F_ALTER: u8 = 1; -pub const F_ZERO: u8 = 1 << 1; -pub const F_LEFT: u8 = 1 << 2; -pub const F_SPACE: u8 = 1 << 3; -pub const F_SIGN: u8 = 1 << 4; -pub const F_GROUP: u8 = 1 << 5; +#[derive(Default, Debug, PartialEq, Eq, Clone, Copy)] +pub struct Flags { + pub alter: bool, + pub zero: bool, + pub left: bool, + pub space: bool, + pub sign: bool, + pub group: bool, +} /// checks if the string is within the specified bound, /// if it gets out of bound, error out by printing sub-string from index `beg` to`end`, @@ -138,7 +141,7 @@ pub enum OutputType { pub enum Token { Char(char), Directive { - flag: u8, + flag: Flags, width: usize, precision: i32, format: char, @@ -245,7 +248,7 @@ pub struct Stater { } #[allow(clippy::cognitive_complexity)] -fn print_it(arg: &str, output_type: &OutputType, flag: u8, width: usize, precision: i32) { +fn print_it(arg: &str, output_type: &OutputType, flags: Flags, width: usize, precision: i32) { // If the precision is given as just '.', the precision is taken to be zero. // A negative precision is taken as if the precision were omitted. // This gives the minimum number of digits to appear for d, i, o, u, x, and X conversions, @@ -280,21 +283,19 @@ fn print_it(arg: &str, output_type: &OutputType, flag: u8, width: usize, precisi return print!("?"); } - let left_align = has!(flag, F_LEFT); - let padding_char: Padding = if has!(flag, F_ZERO) && !left_align && precision == -1 { + let padding_char: Padding = if flags.zero && !flags.left && precision == -1 { Padding::Zero } else { Padding::Space }; - let has_sign = has!(flag, F_SIGN) || has!(flag, F_SPACE); + let has_sign = flags.sign || flags.space; - let should_alter = has!(flag, F_ALTER); let prefix = match output_type { OutputType::UnsignedOct => "0", OutputType::UnsignedHex => "0x", OutputType::Integer => { - if has!(flag, F_SIGN) { + if flags.sign { "+" } else { " " @@ -311,10 +312,10 @@ fn print_it(arg: &str, output_type: &OutputType, flag: u8, width: usize, precisi } else { arg }; - print_adjusted(s, left_align, None, None, width, Padding::Space); + print_adjusted(s, flags.left, None, None, width, Padding::Space); } OutputType::Integer => { - let arg = if has!(flag, F_GROUP) { + let arg = if flags.group { group_num(arg) } else { Cow::Borrowed(arg) @@ -323,7 +324,7 @@ fn print_it(arg: &str, output_type: &OutputType, flag: u8, width: usize, precisi let extended: Cow = extend_digits(arg.as_ref(), min_digits); print_adjusted( extended.as_ref(), - left_align, + flags.left, Some(has_sign), Some(prefix), width, @@ -331,7 +332,7 @@ fn print_it(arg: &str, output_type: &OutputType, flag: u8, width: usize, precisi ); } OutputType::Unsigned => { - let arg = if has!(flag, F_GROUP) { + let arg = if flags.group { group_num(arg) } else { Cow::Borrowed(arg) @@ -340,7 +341,7 @@ fn print_it(arg: &str, output_type: &OutputType, flag: u8, width: usize, precisi let extended: Cow = extend_digits(arg.as_ref(), min_digits); print_adjusted( extended.as_ref(), - left_align, + flags.left, None, None, width, @@ -352,8 +353,8 @@ fn print_it(arg: &str, output_type: &OutputType, flag: u8, width: usize, precisi let extended: Cow = extend_digits(arg, min_digits); print_adjusted( extended.as_ref(), - left_align, - Some(should_alter), + flags.left, + Some(flags.alter), Some(prefix), width, padding_char, @@ -364,8 +365,8 @@ fn print_it(arg: &str, output_type: &OutputType, flag: u8, width: usize, precisi let extended: Cow = extend_digits(arg, min_digits); print_adjusted( extended.as_ref(), - left_align, - Some(should_alter), + flags.left, + Some(flags.alter), Some(prefix), width, padding_char, @@ -397,16 +398,16 @@ impl Stater { continue; } - let mut flag: u8 = 0; + let mut flag = Flags::default(); while i < bound { match chars[i] { - '#' => flag |= F_ALTER, - '0' => flag |= F_ZERO, - '-' => flag |= F_LEFT, - ' ' => flag |= F_SPACE, - '+' => flag |= F_SIGN, - '\'' => flag |= F_GROUP, + '#' => flag.alter = true, + '0' => flag.zero = true, + '-' => flag.left = true, + ' ' => flag.space = true, + '+' => flag.sign = true, + '\'' => flag.group = true, 'I' => unimplemented!(), _ => break, } diff --git a/tests/by-util/test_stat.rs b/tests/by-util/test_stat.rs index 365ac3df3..659e8ee49 100644 --- a/tests/by-util/test_stat.rs +++ b/tests/by-util/test_stat.rs @@ -58,14 +58,22 @@ mod test_generate_tokens { let s = "%'010.2ac%-#5.w\n"; let expected = vec![ Token::Directive { - flag: F_GROUP | F_ZERO, + flag: Flags { + group: true, + zero: true, + ..Default::default() + }, width: 10, precision: 2, format: 'a', }, Token::Char('c'), Token::Directive { - flag: F_LEFT | F_ALTER, + flag: Flags { + left: true, + alter: true, + ..Default::default() + }, width: 5, precision: 0, format: 'w', @@ -80,7 +88,12 @@ mod test_generate_tokens { let s = "%-# 15a\\t\\r\\\"\\\\\\a\\b\\e\\f\\v%+020.-23w\\x12\\167\\132\\112\\n"; let expected = vec![ Token::Directive { - flag: F_LEFT | F_ALTER | F_SPACE, + flag: Flags { + left: true, + alter: true, + space: true, + ..Default::default() + }, width: 15, precision: -1, format: 'a', @@ -95,7 +108,11 @@ mod test_generate_tokens { Token::Char('\x0C'), Token::Char('\x0B'), Token::Directive { - flag: F_SIGN | F_ZERO, + flag: Flags { + sign: true, + zero: true, + ..Default::default() + }, width: 20, precision: -1, format: 'w', From b0224e81457e6aa042d40df53b4e5e308250552c Mon Sep 17 00:00:00 2001 From: Terts Diepraam Date: Wed, 16 Nov 2022 21:17:39 +0100 Subject: [PATCH 3/8] stat: move unit tests to stat.rs instead of test_stat.rs --- src/uu/stat/src/stat.rs | 112 ++++++++++++++++++++++++++++++++++++ tests/by-util/test_stat.rs | 115 ------------------------------------- 2 files changed, 112 insertions(+), 115 deletions(-) diff --git a/src/uu/stat/src/stat.rs b/src/uu/stat/src/stat.rs index 600fdd304..a43836fd3 100644 --- a/src/uu/stat/src/stat.rs +++ b/src/uu/stat/src/stat.rs @@ -1082,3 +1082,115 @@ pub fn uu_app() -> Command { .value_hint(clap::ValueHint::FilePath), ) } + +#[cfg(test)] +mod tests { + use super::{group_num, Flags, ScanUtil, Stater, Token}; + + #[test] + fn test_scanners() { + assert_eq!(Some((-5, 2)), "-5zxc".scan_num::()); + assert_eq!(Some((51, 2)), "51zxc".scan_num::()); + assert_eq!(Some((192, 4)), "+192zxc".scan_num::()); + assert_eq!(None, "z192zxc".scan_num::()); + + assert_eq!(Some(('a', 3)), "141zxc".scan_char(8)); + assert_eq!(Some(('\n', 2)), "12qzxc".scan_char(8)); // spell-checker:disable-line + assert_eq!(Some(('\r', 1)), "dqzxc".scan_char(16)); // spell-checker:disable-line + assert_eq!(None, "z2qzxc".scan_char(8)); // spell-checker:disable-line + } + + #[test] + fn test_group_num() { + assert_eq!("12,379,821,234", group_num("12379821234")); + assert_eq!("21,234", group_num("21234")); + assert_eq!("821,234", group_num("821234")); + assert_eq!("1,821,234", group_num("1821234")); + assert_eq!("1,234", group_num("1234")); + assert_eq!("234", group_num("234")); + assert_eq!("24", group_num("24")); + assert_eq!("4", group_num("4")); + assert_eq!("", group_num("")); + assert_eq!("-5", group_num("-5")); + assert_eq!("-1,234", group_num("-1234")); + } + + #[test] + #[should_panic] + fn test_group_num_panic_if_invalid_numeric_characters() { + group_num("³³³³³"); + } + + #[test] + fn normal_format() { + let s = "%'010.2ac%-#5.w\n"; + let expected = vec![ + Token::Directive { + flag: Flags { + group: true, + zero: true, + ..Default::default() + }, + width: 10, + precision: 2, + format: 'a', + }, + Token::Char('c'), + Token::Directive { + flag: Flags { + left: true, + alter: true, + ..Default::default() + }, + width: 5, + precision: 0, + format: 'w', + }, + Token::Char('\n'), + ]; + assert_eq!(&expected, &Stater::generate_tokens(s, false).unwrap()); + } + + #[test] + fn printf_format() { + let s = "%-# 15a\\t\\r\\\"\\\\\\a\\b\\e\\f\\v%+020.-23w\\x12\\167\\132\\112\\n"; + let expected = vec![ + Token::Directive { + flag: Flags { + left: true, + alter: true, + space: true, + ..Default::default() + }, + width: 15, + precision: -1, + format: 'a', + }, + Token::Char('\t'), + Token::Char('\r'), + Token::Char('"'), + Token::Char('\\'), + Token::Char('\x07'), + Token::Char('\x08'), + Token::Char('\x1B'), + Token::Char('\x0C'), + Token::Char('\x0B'), + Token::Directive { + flag: Flags { + sign: true, + zero: true, + ..Default::default() + }, + width: 20, + precision: -1, + format: 'w', + }, + Token::Char('\x12'), + Token::Char('w'), + Token::Char('Z'), + Token::Char('J'), + Token::Char('\n'), + ]; + assert_eq!(&expected, &Stater::generate_tokens(s, true).unwrap()); + } +} diff --git a/tests/by-util/test_stat.rs b/tests/by-util/test_stat.rs index 659e8ee49..5a6fa41f2 100644 --- a/tests/by-util/test_stat.rs +++ b/tests/by-util/test_stat.rs @@ -7,126 +7,11 @@ extern crate regex; use crate::common::util::*; -extern crate stat; -pub use self::stat::*; - #[test] fn test_invalid_arg() { new_ucmd!().arg("--definitely-invalid").fails().code_is(1); } -#[test] -fn test_scanners() { - assert_eq!(Some((-5, 2)), "-5zxc".scan_num::()); - assert_eq!(Some((51, 2)), "51zxc".scan_num::()); - assert_eq!(Some((192, 4)), "+192zxc".scan_num::()); - assert_eq!(None, "z192zxc".scan_num::()); - - assert_eq!(Some(('a', 3)), "141zxc".scan_char(8)); - assert_eq!(Some(('\n', 2)), "12qzxc".scan_char(8)); // spell-checker:disable-line - assert_eq!(Some(('\r', 1)), "dqzxc".scan_char(16)); // spell-checker:disable-line - assert_eq!(None, "z2qzxc".scan_char(8)); // spell-checker:disable-line -} - -#[test] -fn test_group_num() { - assert_eq!("12,379,821,234", group_num("12379821234")); - assert_eq!("21,234", group_num("21234")); - assert_eq!("821,234", group_num("821234")); - assert_eq!("1,821,234", group_num("1821234")); - assert_eq!("1,234", group_num("1234")); - assert_eq!("234", group_num("234")); - assert_eq!("24", group_num("24")); - assert_eq!("4", group_num("4")); - assert_eq!("", group_num("")); - assert_eq!("-5", group_num("-5")); - assert_eq!("-1,234", group_num("-1234")); -} - -#[test] -#[should_panic] -fn test_group_num_panic_if_invalid_numeric_characters() { - group_num("³³³³³"); -} - -#[cfg(test)] -mod test_generate_tokens { - use super::*; - - #[test] - fn normal_format() { - let s = "%'010.2ac%-#5.w\n"; - let expected = vec![ - Token::Directive { - flag: Flags { - group: true, - zero: true, - ..Default::default() - }, - width: 10, - precision: 2, - format: 'a', - }, - Token::Char('c'), - Token::Directive { - flag: Flags { - left: true, - alter: true, - ..Default::default() - }, - width: 5, - precision: 0, - format: 'w', - }, - Token::Char('\n'), - ]; - assert_eq!(&expected, &Stater::generate_tokens(s, false).unwrap()); - } - - #[test] - fn printf_format() { - let s = "%-# 15a\\t\\r\\\"\\\\\\a\\b\\e\\f\\v%+020.-23w\\x12\\167\\132\\112\\n"; - let expected = vec![ - Token::Directive { - flag: Flags { - left: true, - alter: true, - space: true, - ..Default::default() - }, - width: 15, - precision: -1, - format: 'a', - }, - Token::Char('\t'), - Token::Char('\r'), - Token::Char('"'), - Token::Char('\\'), - Token::Char('\x07'), - Token::Char('\x08'), - Token::Char('\x1B'), - Token::Char('\x0C'), - Token::Char('\x0B'), - Token::Directive { - flag: Flags { - sign: true, - zero: true, - ..Default::default() - }, - width: 20, - precision: -1, - format: 'w', - }, - Token::Char('\x12'), - Token::Char('w'), - Token::Char('Z'), - Token::Char('J'), - Token::Char('\n'), - ]; - assert_eq!(&expected, &Stater::generate_tokens(s, true).unwrap()); - } -} - #[test] fn test_invalid_option() { new_ucmd!().arg("-w").arg("-q").arg("/").fails(); From 406df12b18042e732ddc22244e4fb7b27693f2ec Mon Sep 17 00:00:00 2001 From: Terts Diepraam Date: Wed, 16 Nov 2022 22:23:03 +0100 Subject: [PATCH 4/8] stat: make OutputType carry data, instead of turning everthing immediately into a string --- src/uu/stat/src/stat.rs | 333 ++++++++------------------- src/uucore/src/lib/features/fsext.rs | 8 +- 2 files changed, 102 insertions(+), 239 deletions(-) diff --git a/src/uu/stat/src/stat.rs b/src/uu/stat/src/stat.rs index a43836fd3..03747a709 100644 --- a/src/uu/stat/src/stat.rs +++ b/src/uu/stat/src/stat.rs @@ -127,13 +127,13 @@ fn print_adjusted( pad_and_print(s, left, field_width, padding); } } -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug)] pub enum OutputType { - Str, - Integer, - Unsigned, - UnsignedHex, - UnsignedOct, + Str(String), + Integer(i64), + Unsigned(u64), + UnsignedHex(u64), + UnsignedOct(u32), Unknown, } @@ -248,7 +248,7 @@ pub struct Stater { } #[allow(clippy::cognitive_complexity)] -fn print_it(arg: &str, output_type: &OutputType, flags: Flags, width: usize, precision: i32) { +fn print_it(output: &OutputType, flags: Flags, width: usize, precision: i32) { // If the precision is given as just '.', the precision is taken to be zero. // A negative precision is taken as if the precision were omitted. // This gives the minimum number of digits to appear for d, i, o, u, x, and X conversions, @@ -279,10 +279,6 @@ fn print_it(arg: &str, output_type: &OutputType, flags: Flags, width: usize, pre // By default, a sign is used only for negative numbers. // A + overrides a space if both are used. - if output_type == &OutputType::Unknown { - return print!("?"); - } - let padding_char: Padding = if flags.zero && !flags.left && precision == -1 { Padding::Zero } else { @@ -291,37 +287,22 @@ fn print_it(arg: &str, output_type: &OutputType, flags: Flags, width: usize, pre let has_sign = flags.sign || flags.space; - let prefix = match output_type { - OutputType::UnsignedOct => "0", - OutputType::UnsignedHex => "0x", - OutputType::Integer => { - if flags.sign { - "+" - } else { - " " - } - } - _ => "", - }; - - match output_type { - OutputType::Str => { - let limit = cmp::min(precision, arg.len() as i32); - let s: &str = if limit >= 0 { - &arg[..limit as usize] - } else { - arg - }; + match output { + OutputType::Str(s) => { + let limit = cmp::min(precision, s.len() as i32); + let s: &str = if limit >= 0 { &s[..limit as usize] } else { s }; print_adjusted(s, flags.left, None, None, width, Padding::Space); } - OutputType::Integer => { + OutputType::Integer(num) => { + let num = num.to_string(); let arg = if flags.group { - group_num(arg) + group_num(&num) } else { - Cow::Borrowed(arg) + Cow::Borrowed(num.as_str()) }; let min_digits = cmp::max(precision, arg.len() as i32) as usize; let extended: Cow = extend_digits(arg.as_ref(), min_digits); + let prefix = if flags.sign { "+" } else { " " }; print_adjusted( extended.as_ref(), flags.left, @@ -331,11 +312,12 @@ fn print_it(arg: &str, output_type: &OutputType, flags: Flags, width: usize, pre padding_char, ); } - OutputType::Unsigned => { + OutputType::Unsigned(num) => { + let num = num.to_string(); let arg = if flags.group { - group_num(arg) + group_num(&num) } else { - Cow::Borrowed(arg) + Cow::Borrowed(num.as_str()) }; let min_digits = cmp::max(precision, arg.len() as i32) as usize; let extended: Cow = extend_digits(arg.as_ref(), min_digits); @@ -348,31 +330,33 @@ fn print_it(arg: &str, output_type: &OutputType, flags: Flags, width: usize, pre padding_char, ); } - OutputType::UnsignedOct => { - let min_digits = cmp::max(precision, arg.len() as i32) as usize; - let extended: Cow = extend_digits(arg, min_digits); + OutputType::UnsignedOct(num) => { + let num = format!("{:o}", num); + let min_digits = cmp::max(precision, num.len() as i32) as usize; + let extended: Cow = extend_digits(&num, min_digits); print_adjusted( extended.as_ref(), flags.left, Some(flags.alter), - Some(prefix), + Some("0"), width, padding_char, ); } - OutputType::UnsignedHex => { - let min_digits = cmp::max(precision, arg.len() as i32) as usize; - let extended: Cow = extend_digits(arg, min_digits); + OutputType::UnsignedHex(num) => { + let num = format!("{:x}", num); + let min_digits = cmp::max(precision, num.len() as i32) as usize; + let extended: Cow = extend_digits(&num, min_digits); print_adjusted( extended.as_ref(), flags.left, Some(flags.alter), - Some(prefix), + Some("0x"), width, padding_char, ); } - _ => unreachable!(), + OutputType::Unknown => print!("?"), } } @@ -624,92 +608,49 @@ impl Stater { precision, format, } => { - let arg: String; - let output_type: OutputType; - - match format { + let output = match format { // access rights in octal - 'a' => { - arg = format!("{:o}", 0o7777 & meta.mode()); - output_type = OutputType::UnsignedOct; - } + 'a' => OutputType::UnsignedOct(0o7777 & meta.mode()), // access rights in human readable form - 'A' => { - arg = display_permissions(&meta, true); - output_type = OutputType::Str; - } + 'A' => OutputType::Str(display_permissions(&meta, true)), // number of blocks allocated (see %B) - 'b' => { - arg = format!("{}", meta.blocks()); - output_type = OutputType::Unsigned; - } + 'b' => OutputType::Unsigned(meta.blocks()), // the size in bytes of each block reported by %b // FIXME: blocksize differs on various platform // See coreutils/gnulib/lib/stat-size.h ST_NBLOCKSIZE // spell-checker:disable-line - 'B' => { - // the size in bytes of each block reported by %b - arg = format!("{}", 512); - output_type = OutputType::Unsigned; - } + 'B' => OutputType::Unsigned(512), // device number in decimal - 'd' => { - arg = format!("{}", meta.dev()); - output_type = OutputType::Unsigned; - } + 'd' => OutputType::Unsigned(meta.dev()), // device number in hex - 'D' => { - arg = format!("{:x}", meta.dev()); - output_type = OutputType::UnsignedHex; - } + 'D' => OutputType::UnsignedHex(meta.dev()), // raw mode in hex - 'f' => { - arg = format!("{:x}", meta.mode()); - output_type = OutputType::UnsignedHex; - } + 'f' => OutputType::UnsignedHex(meta.mode() as u64), // file type - 'F' => { - arg = pretty_filetype(meta.mode() as mode_t, meta.len()) - .to_owned(); - output_type = OutputType::Str; - } + 'F' => OutputType::Str( + pretty_filetype(meta.mode() as mode_t, meta.len()) + .to_owned(), + ), // group ID of owner - 'g' => { - arg = format!("{}", meta.gid()); - output_type = OutputType::Unsigned; - } + 'g' => OutputType::Unsigned(meta.gid() as u64), // group name of owner 'G' => { - arg = entries::gid2grp(meta.gid()) + let group_name = entries::gid2grp(meta.gid()) .unwrap_or_else(|_| "UNKNOWN".to_owned()); - output_type = OutputType::Str; + OutputType::Str(group_name) } // number of hard links - 'h' => { - arg = format!("{}", meta.nlink()); - output_type = OutputType::Unsigned; - } + 'h' => OutputType::Unsigned(meta.nlink()), // inode number - 'i' => { - arg = format!("{}", meta.ino()); - output_type = OutputType::Unsigned; - } - + 'i' => OutputType::Unsigned(meta.ino()), // mount point - 'm' => { - arg = self.find_mount_point(&file).unwrap(); - output_type = OutputType::Str; - } - + 'm' => OutputType::Str(self.find_mount_point(&file).unwrap()), // file name - 'n' => { - arg = display_name.to_string(); - output_type = OutputType::Str; - } + 'n' => OutputType::Str(display_name.to_string()), // quoted file name with dereference if symbolic link 'N' => { - if file_type.is_symlink() { + let file_name = if file_type.is_symlink() { let dst = match fs::read_link(&file) { Ok(path) => path, Err(e) => { @@ -717,99 +658,62 @@ impl Stater { return 1; } }; - arg = format!( - "{} -> {}", - display_name.quote(), - dst.quote() - ); + format!("{} -> {}", display_name.quote(), dst.quote()) } else { - arg = display_name.to_string(); - } - output_type = OutputType::Str; + display_name.to_string() + }; + OutputType::Str(file_name) } // optimal I/O transfer size hint - 'o' => { - arg = format!("{}", meta.blksize()); - output_type = OutputType::Unsigned; - } + 'o' => OutputType::Unsigned(meta.blksize()), // total size, in bytes - 's' => { - arg = format!("{}", meta.len()); - output_type = OutputType::Integer; - } + 's' => OutputType::Integer(meta.len() as i64), // major device type in hex, for character/block device special // files - 't' => { - arg = format!("{:x}", meta.rdev() >> 8); - output_type = OutputType::UnsignedHex; - } + 't' => OutputType::UnsignedHex(meta.rdev() >> 8), // minor device type in hex, for character/block device special // files - 'T' => { - arg = format!("{:x}", meta.rdev() & 0xff); - output_type = OutputType::UnsignedHex; - } + 'T' => OutputType::UnsignedHex(meta.rdev() & 0xff), // user ID of owner - 'u' => { - arg = format!("{}", meta.uid()); - output_type = OutputType::Unsigned; - } + 'u' => OutputType::Unsigned(meta.uid() as u64), // user name of owner 'U' => { - arg = entries::uid2usr(meta.uid()) + let user_name = entries::uid2usr(meta.uid()) .unwrap_or_else(|_| "UNKNOWN".to_owned()); - output_type = OutputType::Str; + OutputType::Str(user_name) } // time of file birth, human-readable; - if unknown - 'w' => { - arg = meta.pretty_birth(); - output_type = OutputType::Str; - } + 'w' => OutputType::Str(meta.pretty_birth()), // time of file birth, seconds since Epoch; 0 if unknown - 'W' => { - arg = meta.birth(); - output_type = OutputType::Integer; - } + 'W' => OutputType::Unsigned(meta.birth()), // time of last access, human-readable - 'x' => { - arg = pretty_time(meta.atime(), meta.atime_nsec()); - output_type = OutputType::Str; - } + 'x' => OutputType::Str(pretty_time( + meta.atime(), + meta.atime_nsec(), + )), // time of last access, seconds since Epoch - 'X' => { - arg = format!("{}", meta.atime()); - output_type = OutputType::Integer; - } + 'X' => OutputType::Integer(meta.atime()), // time of last data modification, human-readable - 'y' => { - arg = pretty_time(meta.mtime(), meta.mtime_nsec()); - output_type = OutputType::Str; - } + 'y' => OutputType::Str(pretty_time( + meta.mtime(), + meta.mtime_nsec(), + )), // time of last data modification, seconds since Epoch - 'Y' => { - arg = format!("{}", meta.mtime()); - output_type = OutputType::Str; - } + 'Y' => OutputType::Integer(meta.mtime()), // time of last status change, human-readable - 'z' => { - arg = pretty_time(meta.ctime(), meta.ctime_nsec()); - output_type = OutputType::Str; - } + 'z' => OutputType::Str(pretty_time( + meta.ctime(), + meta.ctime_nsec(), + )), // time of last status change, seconds since Epoch - 'Z' => { - arg = format!("{}", meta.ctime()); - output_type = OutputType::Integer; - } + 'Z' => OutputType::Integer(meta.ctime()), - _ => { - arg = "?".to_owned(); - output_type = OutputType::Unknown; - } - } - print_it(&arg, &output_type, flag, width, precision); + _ => OutputType::Unknown, + }; + print_it(&output, flag, width, precision); } } } @@ -837,76 +741,35 @@ impl Stater { precision, format, } => { - let arg: String; - let output_type: OutputType; - match format { + let output = match format { // free blocks available to non-superuser - 'a' => { - arg = format!("{}", meta.avail_blocks()); - output_type = OutputType::Integer; - } + 'a' => OutputType::Unsigned(meta.avail_blocks()), // total data blocks in file system - 'b' => { - arg = format!("{}", meta.total_blocks()); - output_type = OutputType::Integer; - } + 'b' => OutputType::Unsigned(meta.total_blocks()), // total file nodes in file system - 'c' => { - arg = format!("{}", meta.total_file_nodes()); - output_type = OutputType::Unsigned; - } + 'c' => OutputType::Unsigned(meta.total_file_nodes()), // free file nodes in file system - 'd' => { - arg = format!("{}", meta.free_file_nodes()); - output_type = OutputType::Integer; - } + 'd' => OutputType::Unsigned(meta.free_file_nodes()), // free blocks in file system - 'f' => { - arg = format!("{}", meta.free_blocks()); - output_type = OutputType::Integer; - } + 'f' => OutputType::Unsigned(meta.free_blocks()), // file system ID in hex - 'i' => { - arg = format!("{:x}", meta.fsid()); - output_type = OutputType::UnsignedHex; - } + 'i' => OutputType::UnsignedHex(meta.fsid()), // maximum length of filenames - 'l' => { - arg = format!("{}", meta.namelen()); - output_type = OutputType::Unsigned; - } + 'l' => OutputType::Unsigned(meta.namelen()), // file name - 'n' => { - arg = display_name.to_string(); - output_type = OutputType::Str; - } + 'n' => OutputType::Str(display_name.to_string()), // block size (for faster transfers) - 's' => { - arg = format!("{}", meta.io_size()); - output_type = OutputType::Unsigned; - } + 's' => OutputType::Unsigned(meta.io_size()), // fundamental block size (for block counts) - 'S' => { - arg = format!("{}", meta.block_size()); - output_type = OutputType::Unsigned; - } + 'S' => OutputType::Integer(meta.block_size()), // file system type in hex - 't' => { - arg = format!("{:x}", meta.fs_type()); - output_type = OutputType::UnsignedHex; - } + 't' => OutputType::UnsignedHex(meta.fs_type() as u64), // file system type in human readable form - 'T' => { - arg = pretty_fstype(meta.fs_type()).into_owned(); - output_type = OutputType::Str; - } - _ => { - arg = "?".to_owned(); - output_type = OutputType::Unknown; - } - } + 'T' => OutputType::Str(pretty_fstype(meta.fs_type()).into()), + _ => OutputType::Unknown, + }; - print_it(&arg, &output_type, flag, width, precision); + print_it(&output, flag, width, precision); } } } diff --git a/src/uucore/src/lib/features/fsext.rs b/src/uucore/src/lib/features/fsext.rs index 68e2b7a55..ebfb51ca0 100644 --- a/src/uucore/src/lib/features/fsext.rs +++ b/src/uucore/src/lib/features/fsext.rs @@ -123,7 +123,7 @@ pub use libc::statvfs as statfs_fn; pub trait BirthTime { fn pretty_birth(&self) -> String; - fn birth(&self) -> String; + fn birth(&self) -> u64; } use std::fs::Metadata; @@ -136,12 +136,12 @@ impl BirthTime for Metadata { .unwrap_or_else(|| "-".to_owned()) } - fn birth(&self) -> String { + fn birth(&self) -> u64 { self.created() .ok() .and_then(|t| t.duration_since(UNIX_EPOCH).ok()) - .map(|e| format!("{}", e.as_secs())) - .unwrap_or_else(|| "0".to_owned()) + .map(|e| e.as_secs()) + .unwrap_or_default() } } From b0894f86eddc1a3e5a011e1c7b3f0cc8e001f39f Mon Sep 17 00:00:00 2001 From: Terts Diepraam Date: Wed, 16 Nov 2022 23:40:33 +0100 Subject: [PATCH 5/8] stat: minor cleanup --- src/uu/stat/src/stat.rs | 104 ++++++++++++++++++---------------------- 1 file changed, 47 insertions(+), 57 deletions(-) diff --git a/src/uu/stat/src/stat.rs b/src/uu/stat/src/stat.rs index 03747a709..5ac66ebe1 100644 --- a/src/uu/stat/src/stat.rs +++ b/src/uu/stat/src/stat.rs @@ -26,19 +26,18 @@ use std::os::unix::prelude::OsStrExt; use std::path::Path; use std::{cmp, fs, iter}; -static ABOUT: &str = "Display file or file system status."; +const ABOUT: &str = "Display file or file system status."; const USAGE: &str = "{} [OPTION]... FILE..."; pub mod options { - pub static DEREFERENCE: &str = "dereference"; - pub static FILE_SYSTEM: &str = "file-system"; - pub static FORMAT: &str = "format"; - pub static PRINTF: &str = "printf"; - pub static TERSE: &str = "terse"; + pub const DEREFERENCE: &str = "dereference"; + pub const FILE_SYSTEM: &str = "file-system"; + pub const FORMAT: &str = "format"; + pub const PRINTF: &str = "printf"; + pub const TERSE: &str = "terse"; + pub const FILES: &str = "files"; } -static ARG_FILES: &str = "files"; - #[derive(Default, Debug, PartialEq, Eq, Clone, Copy)] pub struct Flags { pub alter: bool, @@ -117,10 +116,8 @@ fn print_adjusted( ) { let mut field_width = cmp::max(width, s.len()); if let Some(p) = prefix { - if let Some(prefix_flag) = need_prefix { - if prefix_flag { - field_width -= p.len(); - } + if let Some(true) = need_prefix { + field_width -= p.len(); } pad_and_print(s, left, field_width, padding); } else { @@ -163,7 +160,7 @@ impl ScanUtil for str { let mut chars = self.chars(); let mut i = 0; match chars.next() { - Some('-') | Some('+') | Some('0'..='9') => i += 1, + Some('-' | '+' | '0'..='9') => i += 1, _ => return None, } for c in chars { @@ -181,13 +178,13 @@ impl ScanUtil for str { fn scan_char(&self, radix: u32) -> Option<(char, usize)> { let count = match radix { - 8 => 3_usize, + 8 => 3, 16 => 2, _ => return None, }; let chars = self.chars().enumerate(); - let mut res = 0_u32; - let mut offset = 0_usize; + let mut res = 0; + let mut offset = 0; for (i, c) in chars { if i >= count { break; @@ -279,7 +276,7 @@ fn print_it(output: &OutputType, flags: Flags, width: usize, precision: i32) { // By default, a sign is used only for negative numbers. // A + overrides a space if both are used. - let padding_char: Padding = if flags.zero && !flags.left && precision == -1 { + let padding_char = if flags.zero && !flags.left && precision == -1 { Padding::Zero } else { Padding::Space @@ -365,7 +362,7 @@ impl Stater { let mut tokens = Vec::new(); let bound = format_str.len(); let chars = format_str.chars().collect::>(); - let mut i = 0_usize; + let mut i = 0; while i < bound { match chars[i] { '%' => { @@ -399,8 +396,8 @@ impl Stater { } check_bound(format_str, bound, old, i)?; - let mut width = 0_usize; - let mut precision = -1_i32; + let mut width = 0; + let mut precision = -1; let mut j = i; if let Some((field_width, offset)) = format_str[j..].scan_num::() { @@ -488,7 +485,7 @@ impl Stater { fn new(matches: &ArgMatches) -> UResult { let files = matches - .get_many::(ARG_FILES) + .get_many::(options::FILES) .map(|v| v.map(OsString::from).collect()) .unwrap_or_default(); let format_str = if matches.contains_id(options::PRINTF) { @@ -541,15 +538,11 @@ impl Stater { } fn find_mount_point>(&self, p: P) -> Option { - let path = match p.as_ref().canonicalize() { - Ok(s) => s, - Err(_) => return None, - }; - if let Some(ref mount_list) = self.mount_list { - for root in mount_list.iter() { - if path.starts_with(root) { - return Some(root.clone()); - } + let path = p.as_ref().canonicalize().ok()?; + + for root in self.mount_list.as_ref()? { + if path.starts_with(root) { + return Some(root.clone()); } } None @@ -572,7 +565,7 @@ impl Stater { fn do_stat(&self, file: &OsStr, stdin_is_fifo: bool) -> i32 { let display_name = file.to_string_lossy(); - let file: OsString = if cfg!(unix) && display_name.eq("-") { + let file = if cfg!(unix) && display_name == "-" { if let Ok(p) = Path::new("/dev/stdin").canonicalize() { p.into_os_string() } else { @@ -583,7 +576,7 @@ impl Stater { }; if !self.show_fs { - let result = if self.follow || stdin_is_fifo && display_name.eq("-") { + let result = if self.follow || stdin_is_fifo && display_name == "-" { fs::metadata(&file) } else { fs::symlink_metadata(&file) @@ -790,37 +783,35 @@ impl Stater { fn default_format(show_fs: bool, terse: bool, show_dev_type: bool) -> String { // SELinux related format is *ignored* - let mut format_str = String::with_capacity(36); if show_fs { if terse { - format_str.push_str("%n %i %l %t %s %S %b %f %a %c %d\n"); + "%n %i %l %t %s %S %b %f %a %c %d\n".into() } else { - format_str.push_str( - " File: \"%n\"\n ID: %-8i Namelen: %-7l Type: %T\nBlock \ - size: %-10s Fundamental block size: %S\nBlocks: Total: %-10b \ - Free: %-10f Available: %a\nInodes: Total: %-10c Free: %d\n", - ); + " File: \"%n\"\n ID: %-8i Namelen: %-7l Type: %T\nBlock \ + size: %-10s Fundamental block size: %S\nBlocks: Total: %-10b \ + Free: %-10f Available: %a\nInodes: Total: %-10c Free: %d\n" + .into() } } else if terse { - format_str.push_str("%n %s %b %f %u %g %D %i %h %t %T %X %Y %Z %W %o\n"); + "%n %s %b %f %u %g %D %i %h %t %T %X %Y %Z %W %o\n".into() } else { - format_str.push_str(" File: %N\n Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n"); - if show_dev_type { - format_str - .push_str("Device: %Dh/%dd\tInode: %-10i Links: %-5h Device type: %t,%T\n"); - } else { - format_str.push_str("Device: %Dh/%dd\tInode: %-10i Links: %h\n"); - } - format_str.push_str("Access: (%04a/%10.10A) Uid: (%5u/%8U) Gid: (%5g/%8G)\n"); - format_str.push_str("Access: %x\nModify: %y\nChange: %z\n Birth: %w\n"); + [ + " File: %N\n Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n", + if show_dev_type { + "Device: %Dh/%dd\tInode: %-10i Links: %-5h Device type: %t,%T\n" + } else { + "Device: %Dh/%dd\tInode: %-10i Links: %h\n" + }, + "Access: (%04a/%10.10A) Uid: (%5u/%8U) Gid: (%5g/%8G)\n", + "Access: %x\nModify: %y\nChange: %z\n Birth: %w\n", + ] + .join("") } - format_str } } -fn get_long_usage() -> String { - String::from( - " +fn get_long_usage() -> &'static str { + " The valid format sequences for files (without --file-system): %a access rights in octal (note '#' and '0' printf flags) @@ -872,8 +863,7 @@ Valid format sequences for file systems: NOTE: your shell may have its own version of stat, which usually supersedes the version described here. Please refer to your shell's documentation for details about the options it supports. -", - ) +" } #[uucore::main] @@ -939,7 +929,7 @@ pub fn uu_app() -> Command { ), ) .arg( - Arg::new(ARG_FILES) + Arg::new(options::FILES) .action(ArgAction::Append) .value_parser(ValueParser::os_string()) .value_hint(clap::ValueHint::FilePath), @@ -1016,7 +1006,7 @@ mod tests { #[test] fn printf_format() { - let s = "%-# 15a\\t\\r\\\"\\\\\\a\\b\\e\\f\\v%+020.-23w\\x12\\167\\132\\112\\n"; + let s = r#"%-# 15a\t\r\"\\\a\b\e\f\v%+020.-23w\x12\167\132\112\n"#; let expected = vec![ Token::Directive { flag: Flags { From cdb777a2430fedba9a6cfb7d0de717db758589c9 Mon Sep 17 00:00:00 2001 From: Terts Diepraam Date: Wed, 16 Nov 2022 23:47:45 +0100 Subject: [PATCH 6/8] stat: remove pub after moving tests --- src/uu/stat/src/stat.rs | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/uu/stat/src/stat.rs b/src/uu/stat/src/stat.rs index 5ac66ebe1..5b7692215 100644 --- a/src/uu/stat/src/stat.rs +++ b/src/uu/stat/src/stat.rs @@ -29,7 +29,7 @@ use std::{cmp, fs, iter}; const ABOUT: &str = "Display file or file system status."; const USAGE: &str = "{} [OPTION]... FILE..."; -pub mod options { +mod options { pub const DEREFERENCE: &str = "dereference"; pub const FILE_SYSTEM: &str = "file-system"; pub const FORMAT: &str = "format"; @@ -39,13 +39,13 @@ pub mod options { } #[derive(Default, Debug, PartialEq, Eq, Clone, Copy)] -pub struct Flags { - pub alter: bool, - pub zero: bool, - pub left: bool, - pub space: bool, - pub sign: bool, - pub group: bool, +struct Flags { + alter: bool, + zero: bool, + left: bool, + space: bool, + sign: bool, + group: bool, } /// checks if the string is within the specified bound, @@ -135,7 +135,7 @@ pub enum OutputType { } #[derive(Debug, PartialEq, Eq)] -pub enum Token { +enum Token { Char(char), Directive { flag: Flags, @@ -145,7 +145,7 @@ pub enum Token { }, } -pub trait ScanUtil { +trait ScanUtil { fn scan_num(&self) -> Option<(F, usize)> where F: std::str::FromStr; @@ -210,7 +210,7 @@ impl ScanUtil for str { } } -pub fn group_num(s: &str) -> Cow { +fn group_num(s: &str) -> Cow { let is_negative = s.starts_with('-'); assert!(is_negative || s.chars().take(1).all(|c| c.is_ascii_digit())); assert!(s.chars().skip(1).all(|c| c.is_ascii_digit())); @@ -234,7 +234,7 @@ pub fn group_num(s: &str) -> Cow { res.into() } -pub struct Stater { +struct Stater { follow: bool, show_fs: bool, from_user: bool, @@ -358,7 +358,7 @@ fn print_it(output: &OutputType, flags: Flags, width: usize, precision: i32) { } impl Stater { - pub fn generate_tokens(format_str: &str, use_printf: bool) -> UResult> { + fn generate_tokens(format_str: &str, use_printf: bool) -> UResult> { let mut tokens = Vec::new(); let bound = format_str.len(); let chars = format_str.chars().collect::>(); From 3499bdaeacf15953b45ced39e28762b9fb347f95 Mon Sep 17 00:00:00 2001 From: Terts Diepraam Date: Thu, 17 Nov 2022 00:13:20 +0100 Subject: [PATCH 7/8] stat: change precision from i32 to Option --- src/uu/stat/src/stat.rs | 38 ++++++++++++++++++-------------------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/src/uu/stat/src/stat.rs b/src/uu/stat/src/stat.rs index 5b7692215..cf89a7835 100644 --- a/src/uu/stat/src/stat.rs +++ b/src/uu/stat/src/stat.rs @@ -140,7 +140,7 @@ enum Token { Directive { flag: Flags, width: usize, - precision: i32, + precision: Option, format: char, }, } @@ -245,7 +245,7 @@ struct Stater { } #[allow(clippy::cognitive_complexity)] -fn print_it(output: &OutputType, flags: Flags, width: usize, precision: i32) { +fn print_it(output: &OutputType, flags: Flags, width: usize, precision: Option) { // If the precision is given as just '.', the precision is taken to be zero. // A negative precision is taken as if the precision were omitted. // This gives the minimum number of digits to appear for d, i, o, u, x, and X conversions, @@ -276,7 +276,7 @@ fn print_it(output: &OutputType, flags: Flags, width: usize, precision: i32) { // By default, a sign is used only for negative numbers. // A + overrides a space if both are used. - let padding_char = if flags.zero && !flags.left && precision == -1 { + let padding_char = if flags.zero && !flags.left && precision.is_none() { Padding::Zero } else { Padding::Space @@ -286,8 +286,10 @@ fn print_it(output: &OutputType, flags: Flags, width: usize, precision: i32) { match output { OutputType::Str(s) => { - let limit = cmp::min(precision, s.len() as i32); - let s: &str = if limit >= 0 { &s[..limit as usize] } else { s }; + let s = match precision { + Some(p) if p < s.len() => &s[..p], + _ => s, + }; print_adjusted(s, flags.left, None, None, width, Padding::Space); } OutputType::Integer(num) => { @@ -297,8 +299,7 @@ fn print_it(output: &OutputType, flags: Flags, width: usize, precision: i32) { } else { Cow::Borrowed(num.as_str()) }; - let min_digits = cmp::max(precision, arg.len() as i32) as usize; - let extended: Cow = extend_digits(arg.as_ref(), min_digits); + let extended = extend_digits(&arg, precision.unwrap_or(0)); let prefix = if flags.sign { "+" } else { " " }; print_adjusted( extended.as_ref(), @@ -316,8 +317,7 @@ fn print_it(output: &OutputType, flags: Flags, width: usize, precision: i32) { } else { Cow::Borrowed(num.as_str()) }; - let min_digits = cmp::max(precision, arg.len() as i32) as usize; - let extended: Cow = extend_digits(arg.as_ref(), min_digits); + let extended = extend_digits(&arg, precision.unwrap_or(0)); print_adjusted( extended.as_ref(), flags.left, @@ -329,8 +329,7 @@ fn print_it(output: &OutputType, flags: Flags, width: usize, precision: i32) { } OutputType::UnsignedOct(num) => { let num = format!("{:o}", num); - let min_digits = cmp::max(precision, num.len() as i32) as usize; - let extended: Cow = extend_digits(&num, min_digits); + let extended = extend_digits(&num, precision.unwrap_or(0)); print_adjusted( extended.as_ref(), flags.left, @@ -342,8 +341,7 @@ fn print_it(output: &OutputType, flags: Flags, width: usize, precision: i32) { } OutputType::UnsignedHex(num) => { let num = format!("{:x}", num); - let min_digits = cmp::max(precision, num.len() as i32) as usize; - let extended: Cow = extend_digits(&num, min_digits); + let extended = extend_digits(&num, precision.unwrap_or(0)); print_adjusted( extended.as_ref(), flags.left, @@ -397,7 +395,7 @@ impl Stater { check_bound(format_str, bound, old, i)?; let mut width = 0; - let mut precision = -1; + let mut precision = None; let mut j = i; if let Some((field_width, offset)) = format_str[j..].scan_num::() { @@ -413,11 +411,11 @@ impl Stater { match format_str[j..].scan_num::() { Some((value, offset)) => { if value >= 0 { - precision = value; + precision = Some(value as usize); } j += offset; } - None => precision = 0, + None => precision = Some(0), } check_bound(format_str, bound, old, j)?; } @@ -985,7 +983,7 @@ mod tests { ..Default::default() }, width: 10, - precision: 2, + precision: Some(2), format: 'a', }, Token::Char('c'), @@ -996,7 +994,7 @@ mod tests { ..Default::default() }, width: 5, - precision: 0, + precision: Some(0), format: 'w', }, Token::Char('\n'), @@ -1016,7 +1014,7 @@ mod tests { ..Default::default() }, width: 15, - precision: -1, + precision: None, format: 'a', }, Token::Char('\t'), @@ -1035,7 +1033,7 @@ mod tests { ..Default::default() }, width: 20, - precision: -1, + precision: None, format: 'w', }, Token::Char('\x12'), From 82464b703a0faa7505fc8613a545f31a0d7ae9b2 Mon Sep 17 00:00:00 2001 From: Terts Diepraam Date: Thu, 17 Nov 2022 01:25:10 +0100 Subject: [PATCH 8/8] stat: remove extend_digits and print_adjusted in favor of format! --- src/uu/stat/src/stat.rs | 107 ++++++++++------------------------------ 1 file changed, 26 insertions(+), 81 deletions(-) diff --git a/src/uu/stat/src/stat.rs b/src/uu/stat/src/stat.rs index cf89a7835..524114f45 100644 --- a/src/uu/stat/src/stat.rs +++ b/src/uu/stat/src/stat.rs @@ -21,10 +21,10 @@ use clap::{crate_version, Arg, ArgAction, ArgMatches, Command}; use std::borrow::Cow; use std::convert::AsRef; use std::ffi::{OsStr, OsString}; +use std::fs; use std::os::unix::fs::{FileTypeExt, MetadataExt}; use std::os::unix::prelude::OsStrExt; use std::path::Path; -use std::{cmp, fs, iter}; const ABOUT: &str = "Display file or file system status."; const USAGE: &str = "{} [OPTION]... FILE..."; @@ -61,22 +61,6 @@ fn check_bound(slice: &str, bound: usize, beg: usize, end: usize) -> UResult<()> Ok(()) } -/// pads the string with zeroes if supplied min is greater -/// then the length of the string, else returns the original string -fn extend_digits(string: &str, min: usize) -> Cow<'_, str> { - if min > string.len() { - let mut pad = String::with_capacity(min); - iter::repeat('0') - .take(min - string.len()) - .map(|_| pad.push('0')) - .all(|_| true); - pad.push_str(string); - pad.into() - } else { - string.into() - } -} - enum Padding { Zero, Space, @@ -100,30 +84,6 @@ fn pad_and_print(result: &str, left: bool, width: usize, padding: Padding) { }; } -/// prints the adjusted string after padding -/// `left` flag specifies the type of alignment of the string -/// `width` is the supplied padding width of the string needed -/// `prefix` & `need_prefix` are Optional, which adjusts the `field_width` accordingly, where -/// `field_width` is the max of supplied `width` and size of string -/// `padding`, specifies type of padding, which is '0' or ' ' in this case. -fn print_adjusted( - s: &str, - left: bool, - need_prefix: Option, - prefix: Option<&str>, - width: usize, - padding: Padding, -) { - let mut field_width = cmp::max(width, s.len()); - if let Some(p) = prefix { - if let Some(true) = need_prefix { - field_width -= p.len(); - } - pad_and_print(s, left, field_width, padding); - } else { - pad_and_print(s, left, field_width, padding); - } -} #[derive(Debug)] pub enum OutputType { Str(String), @@ -282,15 +242,13 @@ fn print_it(output: &OutputType, flags: Flags, width: usize, precision: Option { let s = match precision { Some(p) if p < s.len() => &s[..p], _ => s, }; - print_adjusted(s, flags.left, None, None, width, Padding::Space); + pad_and_print(s, flags.left, width, Padding::Space); } OutputType::Integer(num) => { let num = num.to_string(); @@ -299,57 +257,44 @@ fn print_it(output: &OutputType, flags: Flags, width: usize, precision: Optionprecision$}", + precision = precision.unwrap_or(0) ); + pad_and_print(&extended, flags.left, width, padding_char); } OutputType::Unsigned(num) => { let num = num.to_string(); - let arg = if flags.group { + let s = if flags.group { group_num(&num) } else { Cow::Borrowed(num.as_str()) }; - let extended = extend_digits(&arg, precision.unwrap_or(0)); - print_adjusted( - extended.as_ref(), - flags.left, - None, - None, - width, - padding_char, - ); + let s = format!("{s:0>precision$}", precision = precision.unwrap_or(0)); + pad_and_print(&s, flags.left, width, padding_char); } OutputType::UnsignedOct(num) => { - let num = format!("{:o}", num); - let extended = extend_digits(&num, precision.unwrap_or(0)); - print_adjusted( - extended.as_ref(), - flags.left, - Some(flags.alter), - Some("0"), - width, - padding_char, + let prefix = if flags.alter { "0" } else { "" }; + let s = format!( + "{prefix}{num:0>precision$o}", + precision = precision.unwrap_or(0) ); + pad_and_print(&s, flags.left, width, padding_char); } OutputType::UnsignedHex(num) => { - let num = format!("{:x}", num); - let extended = extend_digits(&num, precision.unwrap_or(0)); - print_adjusted( - extended.as_ref(), - flags.left, - Some(flags.alter), - Some("0x"), - width, - padding_char, + let prefix = if flags.alter { "0x" } else { "" }; + let s = format!( + "{prefix}{num:0>precision$x}", + precision = precision.unwrap_or(0) ); + pad_and_print(&s, flags.left, width, padding_char); } OutputType::Unknown => print!("?"), }