Skip to content

Commit

Permalink
split: suffix auto widening
Browse files Browse the repository at this point in the history
  • Loading branch information
zhitkoff committed Oct 19, 2023
1 parent 4573eb6 commit 93d85ad
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 24 deletions.
31 changes: 16 additions & 15 deletions src/uu/split/src/filenames.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,9 +121,10 @@ impl<'a> FilenameIterator<'a> {
suffix_length: usize,
suffix_type: SuffixType,
suffix_start: usize,
suffix_auto_widening: bool,
) -> UResult<FilenameIterator<'a>> {
let radix = suffix_type.radix();
let number = if suffix_length == 0 {
let number = if suffix_auto_widening {
Number::DynamicWidth(DynamicWidthNumber::new(radix, suffix_start))
} else {
Number::FixedWidth(
Expand Down Expand Up @@ -171,57 +172,57 @@ mod tests {

Check failure on line 172 in src/uu/split/src/filenames.rs

View workflow job for this annotation

GitHub Actions / Style/format (ubuntu-latest, feat_os_unix)

ERROR: `cargo fmt`: style violation (file:'src/uu/split/src/filenames.rs', line:172; use `cargo fmt -- "src/uu/split/src/filenames.rs"`)
#[test]
fn test_filename_iterator_alphabetic_fixed_width() {
let mut it = FilenameIterator::new("chunk_", ".txt", 2, SuffixType::Alphabetic, 0).unwrap();
let mut it = FilenameIterator::new("chunk_", ".txt", 2, SuffixType::Alphabetic, 0, false).unwrap();
assert_eq!(it.next().unwrap(), "chunk_aa.txt");
assert_eq!(it.next().unwrap(), "chunk_ab.txt");
assert_eq!(it.next().unwrap(), "chunk_ac.txt");

Check failure on line 179 in src/uu/split/src/filenames.rs

View workflow job for this annotation

GitHub Actions / Style/format (ubuntu-latest, feat_os_unix)

ERROR: `cargo fmt`: style violation (file:'src/uu/split/src/filenames.rs', line:179; use `cargo fmt -- "src/uu/split/src/filenames.rs"`)
let mut it = FilenameIterator::new("chunk_", ".txt", 2, SuffixType::Alphabetic, 0).unwrap();
let mut it = FilenameIterator::new("chunk_", ".txt", 2, SuffixType::Alphabetic, 0, false).unwrap();
assert_eq!(it.nth(26 * 26 - 1).unwrap(), "chunk_zz.txt");
assert_eq!(it.next(), None);
}

Check failure on line 184 in src/uu/split/src/filenames.rs

View workflow job for this annotation

GitHub Actions / Style/format (ubuntu-latest, feat_os_unix)

ERROR: `cargo fmt`: style violation (file:'src/uu/split/src/filenames.rs', line:184; use `cargo fmt -- "src/uu/split/src/filenames.rs"`)
#[test]
fn test_filename_iterator_numeric_fixed_width() {
let mut it = FilenameIterator::new("chunk_", ".txt", 2, SuffixType::Decimal, 0).unwrap();
let mut it = FilenameIterator::new("chunk_", ".txt", 2, SuffixType::Decimal, 0, false).unwrap();
assert_eq!(it.next().unwrap(), "chunk_00.txt");
assert_eq!(it.next().unwrap(), "chunk_01.txt");
assert_eq!(it.next().unwrap(), "chunk_02.txt");

Check failure on line 191 in src/uu/split/src/filenames.rs

View workflow job for this annotation

GitHub Actions / Style/format (ubuntu-latest, feat_os_unix)

ERROR: `cargo fmt`: style violation (file:'src/uu/split/src/filenames.rs', line:191; use `cargo fmt -- "src/uu/split/src/filenames.rs"`)
let mut it = FilenameIterator::new("chunk_", ".txt", 2, SuffixType::Decimal, 0).unwrap();
let mut it = FilenameIterator::new("chunk_", ".txt", 2, SuffixType::Decimal, 0, false).unwrap();
assert_eq!(it.nth(10 * 10 - 1).unwrap(), "chunk_99.txt");
assert_eq!(it.next(), None);
}

Check failure on line 196 in src/uu/split/src/filenames.rs

View workflow job for this annotation

GitHub Actions / Style/format (ubuntu-latest, feat_os_unix)

ERROR: `cargo fmt`: style violation (file:'src/uu/split/src/filenames.rs', line:196; use `cargo fmt -- "src/uu/split/src/filenames.rs"`)
#[test]
fn test_filename_iterator_alphabetic_dynamic_width() {
let mut it = FilenameIterator::new("chunk_", ".txt", 0, SuffixType::Alphabetic, 0).unwrap();
let mut it = FilenameIterator::new("chunk_", ".txt", 2, SuffixType::Alphabetic, 0, true).unwrap();
assert_eq!(it.next().unwrap(), "chunk_aa.txt");
assert_eq!(it.next().unwrap(), "chunk_ab.txt");
assert_eq!(it.next().unwrap(), "chunk_ac.txt");

Check failure on line 203 in src/uu/split/src/filenames.rs

View workflow job for this annotation

GitHub Actions / Style/format (ubuntu-latest, feat_os_unix)

ERROR: `cargo fmt`: style violation (file:'src/uu/split/src/filenames.rs', line:203; use `cargo fmt -- "src/uu/split/src/filenames.rs"`)
let mut it = FilenameIterator::new("chunk_", ".txt", 0, SuffixType::Alphabetic, 0).unwrap();
let mut it = FilenameIterator::new("chunk_", ".txt", 2, SuffixType::Alphabetic, 0, true).unwrap();
assert_eq!(it.nth(26 * 25 - 1).unwrap(), "chunk_yz.txt");
assert_eq!(it.next().unwrap(), "chunk_zaaa.txt");
assert_eq!(it.next().unwrap(), "chunk_zaab.txt");
}

Check failure on line 209 in src/uu/split/src/filenames.rs

View workflow job for this annotation

GitHub Actions / Style/format (ubuntu-latest, feat_os_unix)

ERROR: `cargo fmt`: style violation (file:'src/uu/split/src/filenames.rs', line:209; use `cargo fmt -- "src/uu/split/src/filenames.rs"`)
#[test]
fn test_filename_iterator_numeric_dynamic_width() {
let mut it = FilenameIterator::new("chunk_", ".txt", 0, SuffixType::Decimal, 0).unwrap();
let mut it = FilenameIterator::new("chunk_", ".txt", 2, SuffixType::Decimal, 0, true).unwrap();
assert_eq!(it.next().unwrap(), "chunk_00.txt");
assert_eq!(it.next().unwrap(), "chunk_01.txt");
assert_eq!(it.next().unwrap(), "chunk_02.txt");

Check failure on line 216 in src/uu/split/src/filenames.rs

View workflow job for this annotation

GitHub Actions / Style/format (ubuntu-latest, feat_os_unix)

ERROR: `cargo fmt`: style violation (file:'src/uu/split/src/filenames.rs', line:216; use `cargo fmt -- "src/uu/split/src/filenames.rs"`)
let mut it = FilenameIterator::new("chunk_", ".txt", 0, SuffixType::Decimal, 0).unwrap();
let mut it = FilenameIterator::new("chunk_", ".txt", 2, SuffixType::Decimal, 0, true).unwrap();
assert_eq!(it.nth(10 * 9 - 1).unwrap(), "chunk_89.txt");
assert_eq!(it.next().unwrap(), "chunk_9000.txt");
assert_eq!(it.next().unwrap(), "chunk_9001.txt");
}

Check failure on line 222 in src/uu/split/src/filenames.rs

View workflow job for this annotation

GitHub Actions / Style/format (ubuntu-latest, feat_os_unix)

ERROR: `cargo fmt`: style violation (file:'src/uu/split/src/filenames.rs', line:222; use `cargo fmt -- "src/uu/split/src/filenames.rs"`)
#[test]
fn test_filename_iterator_numeric_suffix_decimal() {
let mut it = FilenameIterator::new("chunk_", ".txt", 0, SuffixType::Decimal, 5).unwrap();
let mut it = FilenameIterator::new("chunk_", ".txt", 2, SuffixType::Decimal, 5, true).unwrap();
assert_eq!(it.next().unwrap(), "chunk_05.txt");
assert_eq!(it.next().unwrap(), "chunk_06.txt");
assert_eq!(it.next().unwrap(), "chunk_07.txt");
Expand All @@ -230,27 +231,27 @@ mod tests {
#[test]
fn test_filename_iterator_numeric_suffix_hex() {
let mut it =
FilenameIterator::new("chunk_", ".txt", 0, SuffixType::Hexadecimal, 9).unwrap();
FilenameIterator::new("chunk_", ".txt", 2, SuffixType::Hexadecimal, 9, true).unwrap();
assert_eq!(it.next().unwrap(), "chunk_09.txt");
assert_eq!(it.next().unwrap(), "chunk_0a.txt");
assert_eq!(it.next().unwrap(), "chunk_0b.txt");
}

Check failure on line 239 in src/uu/split/src/filenames.rs

View workflow job for this annotation

GitHub Actions / Style/format (ubuntu-latest, feat_os_unix)

ERROR: `cargo fmt`: style violation (file:'src/uu/split/src/filenames.rs', line:239; use `cargo fmt -- "src/uu/split/src/filenames.rs"`)
#[test]
fn test_filename_iterator_numeric_suffix_err() {
let mut it = FilenameIterator::new("chunk_", ".txt", 3, SuffixType::Decimal, 999).unwrap();
let mut it = FilenameIterator::new("chunk_", ".txt", 3, SuffixType::Decimal, 999, false).unwrap();
assert_eq!(it.next().unwrap(), "chunk_999.txt");
assert!(it.next().is_none());

let it = FilenameIterator::new("chunk_", ".txt", 3, SuffixType::Decimal, 1000);
let it = FilenameIterator::new("chunk_", ".txt", 3, SuffixType::Decimal, 1000, false);
assert!(it.is_err());

let mut it =
FilenameIterator::new("chunk_", ".txt", 3, SuffixType::Hexadecimal, 0xfff).unwrap();
FilenameIterator::new("chunk_", ".txt", 3, SuffixType::Hexadecimal, 0xfff, false).unwrap();
assert_eq!(it.next().unwrap(), "chunk_fff.txt");
assert!(it.next().is_none());

let it = FilenameIterator::new("chunk_", ".txt", 3, SuffixType::Hexadecimal, 0x1000);
let it = FilenameIterator::new("chunk_", ".txt", 3, SuffixType::Hexadecimal, 0x1000, false);
assert!(it.is_err());
}
}
54 changes: 45 additions & 9 deletions src/uu/split/src/split.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ static OPT_NUMERIC_SUFFIXES_SHORT: &str = "-d";
static OPT_HEX_SUFFIXES: &str = "hex-suffixes";
static OPT_HEX_SUFFIXES_SHORT: &str = "-x";
static OPT_SUFFIX_LENGTH: &str = "suffix-length";
static OPT_DEFAULT_SUFFIX_LENGTH: &str = "0";
static OPT_DEFAULT_SUFFIX_LENGTH: &str = "2";
static OPT_VERBOSE: &str = "verbose";
static OPT_SEPARATOR: &str = "separator";
//The ---io and ---io-blksize parameters are consumed and ignored.
Expand Down Expand Up @@ -359,7 +359,7 @@ pub fn uu_app() -> Command {
.allow_hyphen_values(true)
.value_name("N")
.default_value(OPT_DEFAULT_SUFFIX_LENGTH)
.help("use suffixes of fixed length N. 0 implies dynamic length, starting with 2"),
.help("generate suffixes of length N (default 2)"),
)
.arg(
Arg::new(OPT_VERBOSE)
Expand Down Expand Up @@ -671,6 +671,7 @@ impl Strategy {

/// Parse the suffix type from the command-line arguments.
fn suffix_type_from(matches: &ArgMatches) -> Result<(SuffixType, usize), SettingsError> {
let default_suffix_length = OPT_DEFAULT_SUFFIX_LENGTH.parse::<usize>().unwrap();
// Check if the user is specifying one or more than one suffix
// Any combination of suffixes is allowed
// Since all suffixes are setup with 'overrides_with_all()' against themselves and each other,
Expand All @@ -694,9 +695,34 @@ fn suffix_type_from(matches: &ArgMatches) -> Result<(SuffixType, usize), Setting
.map_err(|_| SettingsError::SuffixNotParsable(suffix_start.to_string()))?;
Ok((SuffixType::Hexadecimal, suffix_start))
}
(_, _, true, _) => Ok((SuffixType::Decimal, 0)), // short numeric suffix '-d', default start 0
(_, _, _, true) => Ok((SuffixType::Hexadecimal, 0)), // short hex suffix '-x', default start 0
_ => Ok((SuffixType::Alphabetic, 0)), // no numeric/hex suffix, using default alphabetic
(_, _, true, _) => Ok((SuffixType::Decimal, default_suffix_length)), // short numeric suffix '-d', default length
(_, _, _, true) => Ok((SuffixType::Hexadecimal, default_suffix_length)), // short hex suffix '-x', default length
_ => Ok((SuffixType::Alphabetic, default_suffix_length)), // no numeric/hex suffix, using default alphabetic, default length
}
}

/// Determine if the output file names suffix is allowed to auto-widen,
/// i.e. go beyond suffix_length, when more output files need to be written into
/// Suffix auto-widening rules are:
/// - OFF when suffix start number is specified, with an exception:
/// -- ON with `--number` option and suffix start < number of files
/// - ON when suffix start number is NOT specified
fn is_suffix_auto_widening(
matches: &ArgMatches,
suffix_start: &usize,
strategy: &Strategy,
) -> bool {
if matches.value_source(OPT_NUMERIC_SUFFIXES) == Some(ValueSource::CommandLine)
|| matches.value_source(OPT_HEX_SUFFIXES) == Some(ValueSource::CommandLine)
{
if let Strategy::Number(ref number_type) = strategy {
let chunks = number_type.num_chunks();
(*suffix_start as u64) < chunks
} else {
false
}
} else {
true
}
}

Expand All @@ -709,6 +735,9 @@ struct Settings {
suffix_type: SuffixType,
suffix_length: usize,
suffix_start: usize,
/// Whether or not suffix should automatically widen,
/// i.e. go beyond suffix_length
suffix_auto_widening: bool,
additional_suffix: String,
input: String,
/// When supplied, a shell command to output to instead of xaa, xab …
Expand Down Expand Up @@ -801,10 +830,12 @@ impl Settings {
}
let strategy = Strategy::from(matches, obs_lines).map_err(SettingsError::Strategy)?;
let (suffix_type, suffix_start) = suffix_type_from(matches)?;
let suffix_auto_widening = is_suffix_auto_widening(matches, &suffix_start, &strategy);
let suffix_length_str = matches.get_one::<String>(OPT_SUFFIX_LENGTH).unwrap();
let suffix_length: usize = suffix_length_str
.parse()
.map_err(|_| SettingsError::SuffixNotParsable(suffix_length_str.to_string()))?;

if let Strategy::Number(ref number_type) = strategy {
let chunks = number_type.num_chunks();
if suffix_length != 0 {
Expand Down Expand Up @@ -836,13 +867,12 @@ impl Settings {
};

let result = Self {
suffix_length: suffix_length_str
.parse()
.map_err(|_| SettingsError::SuffixNotParsable(suffix_length_str.to_string()))?,
suffix_length,
suffix_type,
suffix_start,
suffix_auto_widening,
additional_suffix,
verbose: matches.value_source("verbose") == Some(ValueSource::CommandLine),
verbose: matches.value_source(OPT_VERBOSE) == Some(ValueSource::CommandLine),
separator,
strategy,
input: matches.get_one::<String>(ARG_INPUT).unwrap().to_owned(),
Expand Down Expand Up @@ -915,6 +945,7 @@ impl<'a> ByteChunkWriter<'a> {
settings.suffix_length,
settings.suffix_type,
settings.suffix_start,
settings.suffix_auto_widening,
)?;
let filename = filename_iterator
.next()
Expand Down Expand Up @@ -1044,6 +1075,7 @@ impl<'a> LineChunkWriter<'a> {
settings.suffix_length,
settings.suffix_type,
settings.suffix_start,
settings.suffix_auto_widening,
)?;
let filename = filename_iterator
.next()
Expand Down Expand Up @@ -1154,6 +1186,7 @@ impl<'a> LineBytesChunkWriter<'a> {
settings.suffix_length,
settings.suffix_type,
settings.suffix_start,
settings.suffix_auto_widening,
)?;
let filename = filename_iterator
.next()
Expand Down Expand Up @@ -1365,6 +1398,7 @@ where
settings.suffix_length,
settings.suffix_type,
settings.suffix_start,
settings.suffix_auto_widening,
)?;

// Create one writer for each chunk. This will create each
Expand Down Expand Up @@ -1529,6 +1563,7 @@ where
settings.suffix_length,
settings.suffix_type,
settings.suffix_start,
settings.suffix_auto_widening,
)?;

// Create one writer for each chunk. This will create each
Expand Down Expand Up @@ -1643,6 +1678,7 @@ where
settings.suffix_length,
settings.suffix_type,
settings.suffix_start,
settings.suffix_auto_widening,
)
.map_err(|e| io::Error::new(ErrorKind::Other, format!("{e}")))?;

Expand Down

0 comments on commit 93d85ad

Please sign in to comment.