mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-27 11:07:44 +00:00
Merge pull request #4743 from sylvestre/cognitive_complexity
Reduce cognitive complexity: just move some of the contents in functions
This commit is contained in:
commit
e734be6775
4 changed files with 592 additions and 325 deletions
|
@ -11,7 +11,7 @@ use clap::{crate_version, Arg, ArgAction, Command};
|
||||||
use std::cmp;
|
use std::cmp;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{stdin, stdout, Write};
|
use std::io::{stdin, stdout, Write};
|
||||||
use std::io::{BufReader, BufWriter, Read};
|
use std::io::{BufReader, BufWriter, Read, Stdout};
|
||||||
use uucore::display::Quotable;
|
use uucore::display::Quotable;
|
||||||
use uucore::error::{FromIo, UResult, USimpleError};
|
use uucore::error::{FromIo, UResult, USimpleError};
|
||||||
use uucore::{format_usage, help_about, help_usage, show_warning};
|
use uucore::{format_usage, help_about, help_usage, show_warning};
|
||||||
|
@ -60,10 +60,16 @@ pub struct FmtOptions {
|
||||||
goal: usize,
|
goal: usize,
|
||||||
tabwidth: usize,
|
tabwidth: usize,
|
||||||
}
|
}
|
||||||
|
/// Parse the command line arguments and return the list of files and formatting options.
|
||||||
#[uucore::main]
|
///
|
||||||
#[allow(clippy::cognitive_complexity)]
|
/// # Arguments
|
||||||
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
///
|
||||||
|
/// * `args` - Command line arguments.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// A tuple containing a vector of file names and a `FmtOptions` struct.
|
||||||
|
fn parse_arguments(args: impl uucore::Args) -> UResult<(Vec<String>, FmtOptions)> {
|
||||||
let matches = uu_app().try_get_matches_from(args)?;
|
let matches = uu_app().try_get_matches_from(args)?;
|
||||||
|
|
||||||
let mut files: Vec<String> = matches
|
let mut files: Vec<String> = matches
|
||||||
|
@ -177,20 +183,37 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
files.push("-".to_owned());
|
files.push("-".to_owned());
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut ostream = BufWriter::new(stdout());
|
Ok((files, fmt_opts))
|
||||||
|
}
|
||||||
|
|
||||||
for i in files.iter().map(|x| &x[..]) {
|
/// Process the content of a file and format it according to the provided options.
|
||||||
let mut fp = match i {
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `file_name` - The name of the file to process. A value of "-" represents the standard input.
|
||||||
|
/// * `fmt_opts` - A reference to a `FmtOptions` struct containing the formatting options.
|
||||||
|
/// * `ostream` - A mutable reference to a `BufWriter` wrapping the standard output.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// A `UResult<()>` indicating success or failure.
|
||||||
|
fn process_file(
|
||||||
|
file_name: &str,
|
||||||
|
fmt_opts: &FmtOptions,
|
||||||
|
ostream: &mut BufWriter<Stdout>,
|
||||||
|
) -> UResult<()> {
|
||||||
|
let mut fp = match file_name {
|
||||||
"-" => BufReader::new(Box::new(stdin()) as Box<dyn Read + 'static>),
|
"-" => BufReader::new(Box::new(stdin()) as Box<dyn Read + 'static>),
|
||||||
_ => match File::open(i) {
|
_ => match File::open(file_name) {
|
||||||
Ok(f) => BufReader::new(Box::new(f) as Box<dyn Read + 'static>),
|
Ok(f) => BufReader::new(Box::new(f) as Box<dyn Read + 'static>),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
show_warning!("{}: {}", i.maybe_quote(), e);
|
show_warning!("{}: {}", file_name.maybe_quote(), e);
|
||||||
continue;
|
return Ok(());
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
let p_stream = ParagraphStream::new(&fmt_opts, &mut fp);
|
|
||||||
|
let p_stream = ParagraphStream::new(fmt_opts, &mut fp);
|
||||||
for para_result in p_stream {
|
for para_result in p_stream {
|
||||||
match para_result {
|
match para_result {
|
||||||
Err(s) => {
|
Err(s) => {
|
||||||
|
@ -201,7 +224,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
.write_all(b"\n")
|
.write_all(b"\n")
|
||||||
.map_err_context(|| "failed to write output".to_string())?;
|
.map_err_context(|| "failed to write output".to_string())?;
|
||||||
}
|
}
|
||||||
Ok(para) => break_lines(¶, &fmt_opts, &mut ostream)
|
Ok(para) => break_lines(¶, fmt_opts, ostream)
|
||||||
.map_err_context(|| "failed to write output".to_string())?,
|
.map_err_context(|| "failed to write output".to_string())?,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -210,6 +233,18 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
ostream
|
ostream
|
||||||
.flush()
|
.flush()
|
||||||
.map_err_context(|| "failed to write output".to_string())?;
|
.map_err_context(|| "failed to write output".to_string())?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[uucore::main]
|
||||||
|
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
|
let (files, fmt_opts) = parse_arguments(args)?;
|
||||||
|
|
||||||
|
let mut ostream = BufWriter::new(stdout());
|
||||||
|
|
||||||
|
for file_name in &files {
|
||||||
|
process_file(file_name, &fmt_opts, &mut ostream)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -50,31 +50,23 @@ struct Options {
|
||||||
zero: bool,
|
zero: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::cognitive_complexity)]
|
/// Creates a Blake2b hasher instance based on the specified length argument.
|
||||||
fn detect_algo(
|
///
|
||||||
program: &str,
|
/// # Returns
|
||||||
matches: &ArgMatches,
|
///
|
||||||
) -> (&'static str, Box<dyn Digest + 'static>, usize) {
|
/// Returns a tuple containing the algorithm name, the hasher instance, and the output length in bits.
|
||||||
let mut alg: Option<Box<dyn Digest>> = None;
|
///
|
||||||
let mut name: &'static str = "";
|
/// # Panics
|
||||||
let mut output_bits = 0;
|
///
|
||||||
match program {
|
/// Panics if the length is not a multiple of 8 or if it is greater than 512.
|
||||||
"md5sum" => ("MD5", Box::new(Md5::new()) as Box<dyn Digest>, 128),
|
fn create_blake2b(matches: &ArgMatches) -> (&'static str, Box<dyn Digest>, usize) {
|
||||||
"sha1sum" => ("SHA1", Box::new(Sha1::new()) as Box<dyn Digest>, 160),
|
match matches.get_one::<usize>("length") {
|
||||||
"sha224sum" => ("SHA224", Box::new(Sha224::new()) as Box<dyn Digest>, 224),
|
|
||||||
"sha256sum" => ("SHA256", Box::new(Sha256::new()) as Box<dyn Digest>, 256),
|
|
||||||
"sha384sum" => ("SHA384", Box::new(Sha384::new()) as Box<dyn Digest>, 384),
|
|
||||||
"sha512sum" => ("SHA512", Box::new(Sha512::new()) as Box<dyn Digest>, 512),
|
|
||||||
"b2sum" => match matches.get_one::<usize>("length") {
|
|
||||||
// by default, blake2 uses 64 bytes (512 bits)
|
|
||||||
// --length=0 falls back to default behavior
|
|
||||||
Some(0) | None => ("BLAKE2", Box::new(Blake2b::new()) as Box<dyn Digest>, 512),
|
Some(0) | None => ("BLAKE2", Box::new(Blake2b::new()) as Box<dyn Digest>, 512),
|
||||||
Some(length_in_bits) => {
|
Some(length_in_bits) => {
|
||||||
if *length_in_bits > 512 {
|
if *length_in_bits > 512 {
|
||||||
crash!(1, "Invalid length (maximum digest length is 512 bits)")
|
crash!(1, "Invalid length (maximum digest length is 512 bits)")
|
||||||
}
|
}
|
||||||
|
|
||||||
// blake2 output size must be a multiple of 8 bits
|
|
||||||
if length_in_bits % 8 == 0 {
|
if length_in_bits % 8 == 0 {
|
||||||
let length_in_bytes = length_in_bits / 8;
|
let length_in_bytes = length_in_bits / 8;
|
||||||
(
|
(
|
||||||
|
@ -86,9 +78,20 @@ fn detect_algo(
|
||||||
crash!(1, "Invalid length (expected a multiple of 8)")
|
crash!(1, "Invalid length (expected a multiple of 8)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
"b3sum" => ("BLAKE3", Box::new(Blake3::new()) as Box<dyn Digest>, 256),
|
}
|
||||||
"sha3sum" => match matches.get_one::<usize>("bits") {
|
|
||||||
|
/// Creates a SHA3 hasher instance based on the specified bits argument.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// Returns a tuple containing the algorithm name, the hasher instance, and the output length in bits.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if an unsupported output size is provided, or if the `--bits` flag is missing.
|
||||||
|
fn create_sha3(matches: &ArgMatches) -> (&'static str, Box<dyn Digest>, usize) {
|
||||||
|
match matches.get_one::<usize>("bits") {
|
||||||
Some(224) => (
|
Some(224) => (
|
||||||
"SHA3-224",
|
"SHA3-224",
|
||||||
Box::new(Sha3_224::new()) as Box<dyn Digest>,
|
Box::new(Sha3_224::new()) as Box<dyn Digest>,
|
||||||
|
@ -114,7 +117,73 @@ fn detect_algo(
|
||||||
"Invalid output size for SHA3 (expected 224, 256, 384, or 512)"
|
"Invalid output size for SHA3 (expected 224, 256, 384, or 512)"
|
||||||
),
|
),
|
||||||
None => crash!(1, "--bits required for SHA3"),
|
None => crash!(1, "--bits required for SHA3"),
|
||||||
},
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a SHAKE-128 hasher instance based on the specified bits argument.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// Returns a tuple containing the algorithm name, the hasher instance, and the output length in bits.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if the `--bits` flag is missing.
|
||||||
|
fn create_shake128(matches: &ArgMatches) -> (&'static str, Box<dyn Digest>, usize) {
|
||||||
|
match matches.get_one::<usize>("bits") {
|
||||||
|
Some(bits) => (
|
||||||
|
"SHAKE128",
|
||||||
|
Box::new(Shake128::new()) as Box<dyn Digest>,
|
||||||
|
*bits,
|
||||||
|
),
|
||||||
|
None => crash!(1, "--bits required for SHAKE-128"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a SHAKE-256 hasher instance based on the specified bits argument.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// Returns a tuple containing the algorithm name, the hasher instance, and the output length in bits.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if the `--bits` flag is missing.
|
||||||
|
fn create_shake256(matches: &ArgMatches) -> (&'static str, Box<dyn Digest>, usize) {
|
||||||
|
match matches.get_one::<usize>("bits") {
|
||||||
|
Some(bits) => (
|
||||||
|
"SHAKE256",
|
||||||
|
Box::new(Shake256::new()) as Box<dyn Digest>,
|
||||||
|
*bits,
|
||||||
|
),
|
||||||
|
None => crash!(1, "--bits required for SHAKE-256"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Detects the hash algorithm from the program name or command-line arguments.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `program` - A string slice containing the program name.
|
||||||
|
/// * `matches` - A reference to the `ArgMatches` object containing the command-line arguments.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// Returns a tuple containing the algorithm name, the hasher instance, and the output length in bits.
|
||||||
|
fn detect_algo(
|
||||||
|
program: &str,
|
||||||
|
matches: &ArgMatches,
|
||||||
|
) -> (&'static str, Box<dyn Digest + 'static>, usize) {
|
||||||
|
let (name, alg, output_bits) = match program {
|
||||||
|
"md5sum" => ("MD5", Box::new(Md5::new()) as Box<dyn Digest>, 128),
|
||||||
|
"sha1sum" => ("SHA1", Box::new(Sha1::new()) as Box<dyn Digest>, 160),
|
||||||
|
"sha224sum" => ("SHA224", Box::new(Sha224::new()) as Box<dyn Digest>, 224),
|
||||||
|
"sha256sum" => ("SHA256", Box::new(Sha256::new()) as Box<dyn Digest>, 256),
|
||||||
|
"sha384sum" => ("SHA384", Box::new(Sha384::new()) as Box<dyn Digest>, 384),
|
||||||
|
"sha512sum" => ("SHA512", Box::new(Sha512::new()) as Box<dyn Digest>, 512),
|
||||||
|
"b2sum" => create_blake2b(matches),
|
||||||
|
"b3sum" => ("BLAKE3", Box::new(Blake3::new()) as Box<dyn Digest>, 256),
|
||||||
|
"sha3sum" => create_sha3(matches),
|
||||||
"sha3-224sum" => (
|
"sha3-224sum" => (
|
||||||
"SHA3-224",
|
"SHA3-224",
|
||||||
Box::new(Sha3_224::new()) as Box<dyn Digest>,
|
Box::new(Sha3_224::new()) as Box<dyn Digest>,
|
||||||
|
@ -135,32 +204,39 @@ fn detect_algo(
|
||||||
Box::new(Sha3_512::new()) as Box<dyn Digest>,
|
Box::new(Sha3_512::new()) as Box<dyn Digest>,
|
||||||
512,
|
512,
|
||||||
),
|
),
|
||||||
"shake128sum" => match matches.get_one::<usize>("bits") {
|
"shake128sum" => create_shake128(matches),
|
||||||
Some(bits) => (
|
"shake256sum" => create_shake256(matches),
|
||||||
"SHAKE128",
|
_ => create_algorithm_from_flags(matches),
|
||||||
Box::new(Shake128::new()) as Box<dyn Digest>,
|
};
|
||||||
*bits,
|
(name, alg, output_bits)
|
||||||
),
|
}
|
||||||
None => crash!(1, "--bits required for SHAKE-128"),
|
|
||||||
},
|
/// Creates a hasher instance based on the command-line flags.
|
||||||
"shake256sum" => match matches.get_one::<usize>("bits") {
|
///
|
||||||
Some(bits) => (
|
/// # Arguments
|
||||||
"SHAKE256",
|
///
|
||||||
Box::new(Shake256::new()) as Box<dyn Digest>,
|
/// * `matches` - A reference to the `ArgMatches` object containing the command-line arguments.
|
||||||
*bits,
|
///
|
||||||
),
|
/// # Returns
|
||||||
None => crash!(1, "--bits required for SHAKE-256"),
|
///
|
||||||
},
|
/// Returns a tuple containing the algorithm name, the hasher instance, and the output length in bits.
|
||||||
_ => {
|
///
|
||||||
{
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if multiple hash algorithms are specified or if a required flag is missing.
|
||||||
|
fn create_algorithm_from_flags(matches: &ArgMatches) -> (&'static str, Box<dyn Digest>, usize) {
|
||||||
|
let mut alg: Option<Box<dyn Digest>> = None;
|
||||||
|
let mut name: &'static str = "";
|
||||||
|
let mut output_bits = 0;
|
||||||
let mut set_or_crash = |n, val, bits| {
|
let mut set_or_crash = |n, val, bits| {
|
||||||
if alg.is_some() {
|
if alg.is_some() {
|
||||||
crash!(1, "You cannot combine multiple hash algorithms!")
|
crash!(1, "You cannot combine multiple hash algorithms!");
|
||||||
};
|
};
|
||||||
name = n;
|
name = n;
|
||||||
alg = Some(val);
|
alg = Some(val);
|
||||||
output_bits = bits;
|
output_bits = bits;
|
||||||
};
|
};
|
||||||
|
|
||||||
if matches.get_flag("md5") {
|
if matches.get_flag("md5") {
|
||||||
set_or_crash("MD5", Box::new(Md5::new()), 128);
|
set_or_crash("MD5", Box::new(Md5::new()), 128);
|
||||||
}
|
}
|
||||||
|
@ -186,33 +262,8 @@ fn detect_algo(
|
||||||
set_or_crash("BLAKE3", Box::new(Blake3::new()), 256);
|
set_or_crash("BLAKE3", Box::new(Blake3::new()), 256);
|
||||||
}
|
}
|
||||||
if matches.get_flag("sha3") {
|
if matches.get_flag("sha3") {
|
||||||
match matches.get_one::<usize>("bits") {
|
let (n, val, bits) = create_sha3(matches);
|
||||||
Some(224) => set_or_crash(
|
set_or_crash(n, val, bits);
|
||||||
"SHA3-224",
|
|
||||||
Box::new(Sha3_224::new()) as Box<dyn Digest>,
|
|
||||||
224,
|
|
||||||
),
|
|
||||||
Some(256) => set_or_crash(
|
|
||||||
"SHA3-256",
|
|
||||||
Box::new(Sha3_256::new()) as Box<dyn Digest>,
|
|
||||||
256,
|
|
||||||
),
|
|
||||||
Some(384) => set_or_crash(
|
|
||||||
"SHA3-384",
|
|
||||||
Box::new(Sha3_384::new()) as Box<dyn Digest>,
|
|
||||||
384,
|
|
||||||
),
|
|
||||||
Some(512) => set_or_crash(
|
|
||||||
"SHA3-512",
|
|
||||||
Box::new(Sha3_512::new()) as Box<dyn Digest>,
|
|
||||||
512,
|
|
||||||
),
|
|
||||||
Some(_) => crash!(
|
|
||||||
1,
|
|
||||||
"Invalid output size for SHA3 (expected 224, 256, 384, or 512)"
|
|
||||||
),
|
|
||||||
None => crash!(1, "--bits required for SHA3"),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if matches.get_flag("sha3-224") {
|
if matches.get_flag("sha3-224") {
|
||||||
set_or_crash("SHA3-224", Box::new(Sha3_224::new()), 224);
|
set_or_crash("SHA3-224", Box::new(Sha3_224::new()), 224);
|
||||||
|
@ -238,12 +289,10 @@ fn detect_algo(
|
||||||
None => crash!(1, "--bits required for SHAKE-256"),
|
None => crash!(1, "--bits required for SHAKE-256"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
let alg = alg.unwrap_or_else(|| crash!(1, "You must specify hash algorithm!"));
|
let alg = alg.unwrap_or_else(|| crash!(1, "You must specify hash algorithm!"));
|
||||||
(name, alg, output_bits)
|
(name, alg, output_bits)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: return custom error type
|
// TODO: return custom error type
|
||||||
fn parse_bit_num(arg: &str) -> Result<usize, ParseIntError> {
|
fn parse_bit_num(arg: &str) -> Result<usize, ParseIntError> {
|
||||||
|
|
|
@ -682,36 +682,23 @@ fn chown_optional_user_group(path: &Path, b: &Behavior) -> UResult<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Copy one file to a new location, changing metadata.
|
/// Perform backup before overwriting.
|
||||||
///
|
|
||||||
/// Returns a Result type with the Err variant containing the error message.
|
|
||||||
///
|
///
|
||||||
/// # Parameters
|
/// # Parameters
|
||||||
///
|
///
|
||||||
/// _from_ must exist as a non-directory.
|
/// * `to` - The destination file path.
|
||||||
/// _to_ must be a non-existent file, whose parent directory exists.
|
/// * `b` - The behavior configuration.
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Returns
|
||||||
///
|
///
|
||||||
/// If the copy system call fails, we print a verbose error and return an empty error value.
|
/// Returns an Option containing the backup path, or None if backup is not needed.
|
||||||
///
|
///
|
||||||
#[allow(clippy::cognitive_complexity)]
|
fn perform_backup(to: &Path, b: &Behavior) -> UResult<Option<PathBuf>> {
|
||||||
fn copy(from: &Path, to: &Path, b: &Behavior) -> UResult<()> {
|
|
||||||
if b.compare && !need_copy(from, to, b)? {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
// Declare the path here as we may need it for the verbose output below.
|
|
||||||
let mut backup_path = None;
|
|
||||||
|
|
||||||
// Perform backup, if any, before overwriting 'to'
|
|
||||||
//
|
|
||||||
// The codes actually making use of the backup process don't seem to agree
|
|
||||||
// on how best to approach the issue. (mv and ln, for example)
|
|
||||||
if to.exists() {
|
if to.exists() {
|
||||||
if b.verbose {
|
if b.verbose {
|
||||||
println!("removed {}", to.quote());
|
println!("removed {}", to.quote());
|
||||||
}
|
}
|
||||||
backup_path = backup_control::get_backup_path(b.backup_mode, to, &b.suffix);
|
let backup_path = backup_control::get_backup_path(b.backup_mode, to, &b.suffix);
|
||||||
if let Some(ref backup_path) = backup_path {
|
if let Some(ref backup_path) = backup_path {
|
||||||
// TODO!!
|
// TODO!!
|
||||||
if let Err(err) = fs::rename(to, backup_path) {
|
if let Err(err) = fs::rename(to, backup_path) {
|
||||||
|
@ -723,8 +710,24 @@ fn copy(from: &Path, to: &Path, b: &Behavior) -> UResult<()> {
|
||||||
.into());
|
.into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Ok(backup_path)
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Copy a file from one path to another.
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
///
|
||||||
|
/// * `from` - The source file path.
|
||||||
|
/// * `to` - The destination file path.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// Returns an empty Result or an error in case of failure.
|
||||||
|
///
|
||||||
|
fn copy_file(from: &Path, to: &Path) -> UResult<()> {
|
||||||
if from.as_os_str() == "/dev/null" {
|
if from.as_os_str() == "/dev/null" {
|
||||||
/* workaround a limitation of fs::copy
|
/* workaround a limitation of fs::copy
|
||||||
* https://github.com/rust-lang/rust/issues/79390
|
* https://github.com/rust-lang/rust/issues/79390
|
||||||
|
@ -737,8 +740,21 @@ fn copy(from: &Path, to: &Path, b: &Behavior) -> UResult<()> {
|
||||||
} else if let Err(err) = fs::copy(from, to) {
|
} else if let Err(err) = fs::copy(from, to) {
|
||||||
return Err(InstallError::InstallFailed(from.to_path_buf(), to.to_path_buf(), err).into());
|
return Err(InstallError::InstallFailed(from.to_path_buf(), to.to_path_buf(), err).into());
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
if b.strip && cfg!(not(windows)) {
|
/// Strip a file using an external program.
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
///
|
||||||
|
/// * `to` - The destination file path.
|
||||||
|
/// * `b` - The behavior configuration.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// Returns an empty Result or an error in case of failure.
|
||||||
|
///
|
||||||
|
fn strip_file(to: &Path, b: &Behavior) -> UResult<()> {
|
||||||
match process::Command::new(&b.strip_program).arg(to).output() {
|
match process::Command::new(&b.strip_program).arg(to).output() {
|
||||||
Ok(o) => {
|
Ok(o) => {
|
||||||
if !o.status.success() {
|
if !o.status.success() {
|
||||||
|
@ -756,8 +772,21 @@ fn copy(from: &Path, to: &Path, b: &Behavior) -> UResult<()> {
|
||||||
return Err(InstallError::StripProgramFailed(e.to_string()).into());
|
return Err(InstallError::StripProgramFailed(e.to_string()).into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set ownership and permissions on the destination file.
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
///
|
||||||
|
/// * `to` - The destination file path.
|
||||||
|
/// * `b` - The behavior configuration.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// Returns an empty Result or an error in case of failure.
|
||||||
|
///
|
||||||
|
fn set_ownership_and_permissions(to: &Path, b: &Behavior) -> UResult<()> {
|
||||||
// Silent the warning as we want to the error message
|
// Silent the warning as we want to the error message
|
||||||
#[allow(clippy::question_mark)]
|
#[allow(clippy::question_mark)]
|
||||||
if mode::chmod(to, b.mode()).is_err() {
|
if mode::chmod(to, b.mode()).is_err() {
|
||||||
|
@ -766,7 +795,21 @@ fn copy(from: &Path, to: &Path, b: &Behavior) -> UResult<()> {
|
||||||
|
|
||||||
chown_optional_user_group(to, b)?;
|
chown_optional_user_group(to, b)?;
|
||||||
|
|
||||||
if b.preserve_timestamps {
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Preserve timestamps on the destination file.
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
///
|
||||||
|
/// * `from` - The source file path.
|
||||||
|
/// * `to` - The destination file path.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// Returns an empty Result or an error in case of failure.
|
||||||
|
///
|
||||||
|
fn preserve_timestamps(from: &Path, to: &Path) -> UResult<()> {
|
||||||
let meta = match fs::metadata(from) {
|
let meta = match fs::metadata(from) {
|
||||||
Ok(meta) => meta,
|
Ok(meta) => meta,
|
||||||
Err(e) => return Err(InstallError::MetadataFailed(e).into()),
|
Err(e) => return Err(InstallError::MetadataFailed(e).into()),
|
||||||
|
@ -776,10 +819,46 @@ fn copy(from: &Path, to: &Path, b: &Behavior) -> UResult<()> {
|
||||||
let accessed_time = FileTime::from_last_access_time(&meta);
|
let accessed_time = FileTime::from_last_access_time(&meta);
|
||||||
|
|
||||||
match set_file_times(to, accessed_time, modified_time) {
|
match set_file_times(to, accessed_time, modified_time) {
|
||||||
Ok(_) => {}
|
Ok(_) => Ok(()),
|
||||||
Err(e) => show_error!("{}", e),
|
Err(e) => {
|
||||||
|
show_error!("{}", e);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Copy one file to a new location, changing metadata.
|
||||||
|
///
|
||||||
|
/// Returns a Result type with the Err variant containing the error message.
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
///
|
||||||
|
/// _from_ must exist as a non-directory.
|
||||||
|
/// _to_ must be a non-existent file, whose parent directory exists.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// If the copy system call fails, we print a verbose error and return an empty error value.
|
||||||
|
///
|
||||||
|
fn copy(from: &Path, to: &Path, b: &Behavior) -> UResult<()> {
|
||||||
|
if b.compare && !need_copy(from, to, b)? {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
// Declare the path here as we may need it for the verbose output below.
|
||||||
|
let backup_path = perform_backup(to, b)?;
|
||||||
|
|
||||||
|
copy_file(from, to)?;
|
||||||
|
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
if b.strip {
|
||||||
|
strip_file(to, b)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
set_ownership_and_permissions(to, b)?;
|
||||||
|
|
||||||
|
if b.preserve_timestamps {
|
||||||
|
preserve_timestamps(from, to)?;
|
||||||
|
}
|
||||||
|
|
||||||
if b.verbose {
|
if b.verbose {
|
||||||
print!("{} -> {}", from.quote(), to.quote());
|
print!("{} -> {}", from.quote(), to.quote());
|
||||||
|
|
|
@ -205,7 +205,16 @@ struct Stater {
|
||||||
default_dev_tokens: Vec<Token>,
|
default_dev_tokens: Vec<Token>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::cognitive_complexity)]
|
/// Prints a formatted output based on the provided output type, flags, width, and precision.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `output` - A reference to the OutputType enum containing the value to be printed.
|
||||||
|
/// * `flags` - A Flags struct containing formatting flags.
|
||||||
|
/// * `width` - The width of the field for the printed output.
|
||||||
|
/// * `precision` - An Option containing the precision value.
|
||||||
|
///
|
||||||
|
/// This function delegates the printing process to more specialized functions depending on the output type.
|
||||||
fn print_it(output: &OutputType, flags: Flags, width: usize, precision: Option<usize>) {
|
fn print_it(output: &OutputType, flags: Flags, width: usize, precision: Option<usize>) {
|
||||||
// If the precision is given as just '.', the precision is taken to be zero.
|
// 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.
|
// A negative precision is taken as if the precision were omitted.
|
||||||
|
@ -236,22 +245,72 @@ fn print_it(output: &OutputType, flags: Flags, width: usize, precision: Option<u
|
||||||
// A sign (+ or -) should always be placed before a number produced by a signed conversion.
|
// A sign (+ or -) should always be placed before a number produced by a signed conversion.
|
||||||
// By default, a sign is used only for negative numbers.
|
// By default, a sign is used only for negative numbers.
|
||||||
// A + overrides a space if both are used.
|
// A + overrides a space if both are used.
|
||||||
|
let padding_char = determine_padding_char(&flags, &precision);
|
||||||
|
|
||||||
let padding_char = if flags.zero && !flags.left && precision.is_none() {
|
match output {
|
||||||
|
OutputType::Str(s) => print_str(s, &flags, width, precision),
|
||||||
|
OutputType::Integer(num) => print_integer(*num, &flags, width, precision, padding_char),
|
||||||
|
OutputType::Unsigned(num) => print_unsigned(*num, &flags, width, precision, padding_char),
|
||||||
|
OutputType::UnsignedOct(num) => {
|
||||||
|
print_unsigned_oct(*num, &flags, width, precision, padding_char);
|
||||||
|
}
|
||||||
|
OutputType::UnsignedHex(num) => {
|
||||||
|
print_unsigned_hex(*num, &flags, width, precision, padding_char);
|
||||||
|
}
|
||||||
|
OutputType::Unknown => print!("?"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Determines the padding character based on the provided flags and precision.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `flags` - A reference to the Flags struct containing formatting flags.
|
||||||
|
/// * `precision` - An Option containing the precision value.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * Padding - An instance of the Padding enum representing the padding character.
|
||||||
|
fn determine_padding_char(flags: &Flags, precision: &Option<usize>) -> Padding {
|
||||||
|
if flags.zero && !flags.left && precision.is_none() {
|
||||||
Padding::Zero
|
Padding::Zero
|
||||||
} else {
|
} else {
|
||||||
Padding::Space
|
Padding::Space
|
||||||
};
|
}
|
||||||
|
}
|
||||||
|
|
||||||
match output {
|
/// Prints a string value based on the provided flags, width, and precision.
|
||||||
OutputType::Str(s) => {
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `s` - The string to be printed.
|
||||||
|
/// * `flags` - A reference to the Flags struct containing formatting flags.
|
||||||
|
/// * `width` - The width of the field for the printed string.
|
||||||
|
/// * `precision` - An Option containing the precision value.
|
||||||
|
fn print_str(s: &str, flags: &Flags, width: usize, precision: Option<usize>) {
|
||||||
let s = match precision {
|
let s = match precision {
|
||||||
Some(p) if p < s.len() => &s[..p],
|
Some(p) if p < s.len() => &s[..p],
|
||||||
_ => s,
|
_ => s,
|
||||||
};
|
};
|
||||||
pad_and_print(s, flags.left, width, Padding::Space);
|
pad_and_print(s, flags.left, width, Padding::Space);
|
||||||
}
|
}
|
||||||
OutputType::Integer(num) => {
|
|
||||||
|
/// Prints an integer value based on the provided flags, width, and precision.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `num` - The integer value to be printed.
|
||||||
|
/// * `flags` - A reference to the Flags struct containing formatting flags.
|
||||||
|
/// * `width` - The width of the field for the printed integer.
|
||||||
|
/// * `precision` - An Option containing the precision value.
|
||||||
|
/// * `padding_char` - The padding character as determined by `determine_padding_char`.
|
||||||
|
fn print_integer(
|
||||||
|
num: i64,
|
||||||
|
flags: &Flags,
|
||||||
|
width: usize,
|
||||||
|
precision: Option<usize>,
|
||||||
|
padding_char: Padding,
|
||||||
|
) {
|
||||||
let num = num.to_string();
|
let num = num.to_string();
|
||||||
let arg = if flags.group {
|
let arg = if flags.group {
|
||||||
group_num(&num)
|
group_num(&num)
|
||||||
|
@ -271,7 +330,23 @@ fn print_it(output: &OutputType, flags: Flags, width: usize, precision: Option<u
|
||||||
);
|
);
|
||||||
pad_and_print(&extended, flags.left, width, padding_char);
|
pad_and_print(&extended, flags.left, width, padding_char);
|
||||||
}
|
}
|
||||||
OutputType::Unsigned(num) => {
|
|
||||||
|
/// Prints an unsigned integer value based on the provided flags, width, and precision.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `num` - The unsigned integer value to be printed.
|
||||||
|
/// * `flags` - A reference to the Flags struct containing formatting flags.
|
||||||
|
/// * `width` - The width of the field for the printed unsigned integer.
|
||||||
|
/// * `precision` - An Option containing the precision value.
|
||||||
|
/// * `padding_char` - The padding character as determined by `determine_padding_char`.
|
||||||
|
fn print_unsigned(
|
||||||
|
num: u64,
|
||||||
|
flags: &Flags,
|
||||||
|
width: usize,
|
||||||
|
precision: Option<usize>,
|
||||||
|
padding_char: Padding,
|
||||||
|
) {
|
||||||
let num = num.to_string();
|
let num = num.to_string();
|
||||||
let s = if flags.group {
|
let s = if flags.group {
|
||||||
group_num(&num)
|
group_num(&num)
|
||||||
|
@ -281,7 +356,23 @@ fn print_it(output: &OutputType, flags: Flags, width: usize, precision: Option<u
|
||||||
let s = format!("{s:0>precision$}", precision = precision.unwrap_or(0));
|
let s = format!("{s:0>precision$}", precision = precision.unwrap_or(0));
|
||||||
pad_and_print(&s, flags.left, width, padding_char);
|
pad_and_print(&s, flags.left, width, padding_char);
|
||||||
}
|
}
|
||||||
OutputType::UnsignedOct(num) => {
|
|
||||||
|
/// Prints an unsigned octal integer value based on the provided flags, width, and precision.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `num` - The unsigned octal integer value to be printed.
|
||||||
|
/// * `flags` - A reference to the Flags struct containing formatting flags.
|
||||||
|
/// * `width` - The width of the field for the printed unsigned octal integer.
|
||||||
|
/// * `precision` - An Option containing the precision value.
|
||||||
|
/// * `padding_char` - The padding character as determined by `determine_padding_char`.
|
||||||
|
fn print_unsigned_oct(
|
||||||
|
num: u32,
|
||||||
|
flags: &Flags,
|
||||||
|
width: usize,
|
||||||
|
precision: Option<usize>,
|
||||||
|
padding_char: Padding,
|
||||||
|
) {
|
||||||
let prefix = if flags.alter { "0" } else { "" };
|
let prefix = if flags.alter { "0" } else { "" };
|
||||||
let s = format!(
|
let s = format!(
|
||||||
"{prefix}{num:0>precision$o}",
|
"{prefix}{num:0>precision$o}",
|
||||||
|
@ -289,7 +380,23 @@ fn print_it(output: &OutputType, flags: Flags, width: usize, precision: Option<u
|
||||||
);
|
);
|
||||||
pad_and_print(&s, flags.left, width, padding_char);
|
pad_and_print(&s, flags.left, width, padding_char);
|
||||||
}
|
}
|
||||||
OutputType::UnsignedHex(num) => {
|
|
||||||
|
/// Prints an unsigned hexadecimal integer value based on the provided flags, width, and precision.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `num` - The unsigned hexadecimal integer value to be printed.
|
||||||
|
/// * `flags` - A reference to the Flags struct containing formatting flags.
|
||||||
|
/// * `width` - The width of the field for the printed unsigned hexadecimal integer.
|
||||||
|
/// * `precision` - An Option containing the precision value.
|
||||||
|
/// * `padding_char` - The padding character as determined by `determine_padding_char`.
|
||||||
|
fn print_unsigned_hex(
|
||||||
|
num: u64,
|
||||||
|
flags: &Flags,
|
||||||
|
width: usize,
|
||||||
|
precision: Option<usize>,
|
||||||
|
padding_char: Padding,
|
||||||
|
) {
|
||||||
let prefix = if flags.alter { "0x" } else { "" };
|
let prefix = if flags.alter { "0x" } else { "" };
|
||||||
let s = format!(
|
let s = format!(
|
||||||
"{prefix}{num:0>precision$x}",
|
"{prefix}{num:0>precision$x}",
|
||||||
|
@ -297,9 +404,6 @@ fn print_it(output: &OutputType, flags: Flags, width: usize, precision: Option<u
|
||||||
);
|
);
|
||||||
pad_and_print(&s, flags.left, width, padding_char);
|
pad_and_print(&s, flags.left, width, padding_char);
|
||||||
}
|
}
|
||||||
OutputType::Unknown => print!("?"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Stater {
|
impl Stater {
|
||||||
fn generate_tokens(format_str: &str, use_printf: bool) -> UResult<Vec<Token>> {
|
fn generate_tokens(format_str: &str, use_printf: bool) -> UResult<Vec<Token>> {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue