mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-30 12:37:49 +00:00
truncate: add parse_mode_and_size() helper func
Add a helper function to contain the code for parsing the size and the modifier symbol, if any. This commit also changes the `TruncateMode` enum so that the parameter for each "mode" is stored along with the enumeration value. This is because the parameter has a different meaning in each mode.
This commit is contained in:
parent
b898e54d7a
commit
005bc259da
1 changed files with 85 additions and 51 deletions
|
@ -15,16 +15,16 @@ use std::fs::{metadata, OpenOptions};
|
||||||
use std::io::ErrorKind;
|
use std::io::ErrorKind;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
#[derive(Eq, PartialEq)]
|
#[derive(Debug, Eq, PartialEq)]
|
||||||
enum TruncateMode {
|
enum TruncateMode {
|
||||||
Absolute,
|
Reference(u64),
|
||||||
Reference,
|
Absolute(u64),
|
||||||
Extend,
|
Extend(u64),
|
||||||
Reduce,
|
Reduce(u64),
|
||||||
AtMost,
|
AtMost(u64),
|
||||||
AtLeast,
|
AtLeast(u64),
|
||||||
RoundDown,
|
RoundDown(u64),
|
||||||
RoundUp,
|
RoundUp(u64),
|
||||||
}
|
}
|
||||||
|
|
||||||
static ABOUT: &str = "Shrink or extend the size of each file to the specified size.";
|
static ABOUT: &str = "Shrink or extend the size of each file to the specified size.";
|
||||||
|
@ -133,46 +133,21 @@ fn truncate(
|
||||||
size: Option<String>,
|
size: Option<String>,
|
||||||
filenames: Vec<String>,
|
filenames: Vec<String>,
|
||||||
) {
|
) {
|
||||||
let (modsize, mode) = match size {
|
let mode = match size {
|
||||||
Some(size_string) => {
|
Some(size_string) => match parse_mode_and_size(&size_string) {
|
||||||
// Trim any whitespace.
|
Ok(m) => m,
|
||||||
let size_string = size_string.trim();
|
Err(_) => crash!(1, "Invalid number: ‘{}’", size_string),
|
||||||
|
},
|
||||||
// Get the modifier character from the size string, if any. For
|
None => TruncateMode::Reference(0),
|
||||||
// example, if the argument is "+123", then the modifier is '+'.
|
|
||||||
let c = size_string.chars().next().unwrap();
|
|
||||||
|
|
||||||
let mode = match c {
|
|
||||||
'+' => TruncateMode::Extend,
|
|
||||||
'-' => TruncateMode::Reduce,
|
|
||||||
'<' => TruncateMode::AtMost,
|
|
||||||
'>' => TruncateMode::AtLeast,
|
|
||||||
'/' => TruncateMode::RoundDown,
|
|
||||||
'%' => TruncateMode::RoundUp,
|
|
||||||
_ => TruncateMode::Absolute, /* assume that the size is just a number */
|
|
||||||
};
|
|
||||||
|
|
||||||
// If there was a modifier character, strip it.
|
|
||||||
let size_string = match mode {
|
|
||||||
TruncateMode::Absolute => size_string,
|
|
||||||
_ => &size_string[1..],
|
|
||||||
};
|
|
||||||
let num_bytes = match parse_size(size_string) {
|
|
||||||
Ok(b) => b,
|
|
||||||
Err(_) => crash!(1, "Invalid number: ‘{}’", size_string),
|
|
||||||
};
|
|
||||||
(num_bytes, mode)
|
|
||||||
}
|
|
||||||
None => (0, TruncateMode::Reference),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let refsize = match reference {
|
let refsize = match reference {
|
||||||
Some(ref rfilename) => {
|
Some(ref rfilename) => {
|
||||||
match mode {
|
match mode {
|
||||||
// Only Some modes work with a reference
|
// Only Some modes work with a reference
|
||||||
TruncateMode::Reference => (), //No --size was given
|
TruncateMode::Reference(_) => (), //No --size was given
|
||||||
TruncateMode::Extend => (),
|
TruncateMode::Extend(_) => (),
|
||||||
TruncateMode::Reduce => (),
|
TruncateMode::Reduce(_) => (),
|
||||||
_ => crash!(1, "you must specify a relative ‘--size’ with ‘--reference’"),
|
_ => crash!(1, "you must specify a relative ‘--size’ with ‘--reference’"),
|
||||||
};
|
};
|
||||||
match metadata(rfilename) {
|
match metadata(rfilename) {
|
||||||
|
@ -202,14 +177,14 @@ fn truncate(
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
let tsize: u64 = match mode {
|
let tsize: u64 = match mode {
|
||||||
TruncateMode::Absolute => modsize,
|
TruncateMode::Absolute(modsize) => modsize,
|
||||||
TruncateMode::Reference => fsize,
|
TruncateMode::Reference(_) => fsize,
|
||||||
TruncateMode::Extend => fsize + modsize,
|
TruncateMode::Extend(modsize) => fsize + modsize,
|
||||||
TruncateMode::Reduce => fsize - modsize,
|
TruncateMode::Reduce(modsize) => fsize - modsize,
|
||||||
TruncateMode::AtMost => fsize.min(modsize),
|
TruncateMode::AtMost(modsize) => fsize.min(modsize),
|
||||||
TruncateMode::AtLeast => fsize.max(modsize),
|
TruncateMode::AtLeast(modsize) => fsize.max(modsize),
|
||||||
TruncateMode::RoundDown => fsize - fsize % modsize,
|
TruncateMode::RoundDown(modsize) => fsize - fsize % modsize,
|
||||||
TruncateMode::RoundUp => fsize + fsize % modsize,
|
TruncateMode::RoundUp(modsize) => fsize + fsize % modsize,
|
||||||
};
|
};
|
||||||
match file.set_len(tsize) {
|
match file.set_len(tsize) {
|
||||||
Ok(_) => {}
|
Ok(_) => {}
|
||||||
|
@ -221,6 +196,52 @@ fn truncate(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Decide whether a character is one of the size modifiers, like '+' or '<'.
|
||||||
|
fn is_modifier(c: char) -> bool {
|
||||||
|
c == '+' || c == '-' || c == '<' || c == '>' || c == '/' || c == '%'
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parse a size string with optional modifier symbol as its first character.
|
||||||
|
///
|
||||||
|
/// A size string is as described in [`parse_size`]. The first character
|
||||||
|
/// of `size_string` might be a modifier symbol, like `'+'` or
|
||||||
|
/// `'<'`. The first element of the pair returned by this function
|
||||||
|
/// indicates which modifier symbol was present, or
|
||||||
|
/// [`TruncateMode::Absolute`] if none.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// If `size_string` is empty, or if no number could be parsed from the
|
||||||
|
/// given string (for example, if the string were `"abc"`).
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust,ignore
|
||||||
|
/// assert_eq!(parse_mode_and_size("+123"), (TruncateMode::Extend, 123));
|
||||||
|
/// ```
|
||||||
|
fn parse_mode_and_size(size_string: &str) -> Result<TruncateMode, ()> {
|
||||||
|
// Trim any whitespace.
|
||||||
|
let size_string = size_string.trim();
|
||||||
|
|
||||||
|
// Get the modifier character from the size string, if any. For
|
||||||
|
// example, if the argument is "+123", then the modifier is '+'.
|
||||||
|
let c = size_string.chars().next().unwrap();
|
||||||
|
let size_string = if is_modifier(c) {
|
||||||
|
&size_string[1..]
|
||||||
|
} else {
|
||||||
|
size_string
|
||||||
|
};
|
||||||
|
parse_size(size_string).map(match c {
|
||||||
|
'+' => TruncateMode::Extend,
|
||||||
|
'-' => TruncateMode::Reduce,
|
||||||
|
'<' => TruncateMode::AtMost,
|
||||||
|
'>' => TruncateMode::AtLeast,
|
||||||
|
'/' => TruncateMode::RoundDown,
|
||||||
|
'%' => TruncateMode::RoundUp,
|
||||||
|
_ => TruncateMode::Absolute,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// Parse a size string into a number of bytes.
|
/// Parse a size string into a number of bytes.
|
||||||
///
|
///
|
||||||
/// A size string comprises an integer and an optional unit. The unit
|
/// A size string comprises an integer and an optional unit. The unit
|
||||||
|
@ -280,7 +301,9 @@ fn parse_size(size: &str) -> Result<u64, ()> {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use crate::parse_mode_and_size;
|
||||||
use crate::parse_size;
|
use crate::parse_size;
|
||||||
|
use crate::TruncateMode;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_size_zero() {
|
fn test_parse_size_zero() {
|
||||||
|
@ -306,4 +329,15 @@ mod tests {
|
||||||
assert_eq!(parse_size("123M").unwrap(), 123 * 1024 * 1024);
|
assert_eq!(parse_size("123M").unwrap(), 123 * 1024 * 1024);
|
||||||
assert_eq!(parse_size("123MB").unwrap(), 123 * 1000 * 1000);
|
assert_eq!(parse_size("123MB").unwrap(), 123 * 1000 * 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_mode_and_size() {
|
||||||
|
assert_eq!(parse_mode_and_size("10"), Ok(TruncateMode::Absolute(10)));
|
||||||
|
assert_eq!(parse_mode_and_size("+10"), Ok(TruncateMode::Extend(10)));
|
||||||
|
assert_eq!(parse_mode_and_size("-10"), Ok(TruncateMode::Reduce(10)));
|
||||||
|
assert_eq!(parse_mode_and_size("<10"), Ok(TruncateMode::AtMost(10)));
|
||||||
|
assert_eq!(parse_mode_and_size(">10"), Ok(TruncateMode::AtLeast(10)));
|
||||||
|
assert_eq!(parse_mode_and_size("/10"), Ok(TruncateMode::RoundDown(10)));
|
||||||
|
assert_eq!(parse_mode_and_size("%10"), Ok(TruncateMode::RoundUp(10)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue