remove use_small_heuristics = "Max" from rustfmt

and update edition to 2021
This commit is contained in:
João M. Bezerra 2022-06-04 13:06:22 -03:00
parent f5fcf7f2a3
commit 14025c6816
15 changed files with 239 additions and 71 deletions

View File

@ -2,11 +2,10 @@
max_width = 120
use_field_init_shorthand = true
newline_style = "Unix"
edition = "2018"
edition = "2021"
reorder_imports = true
reorder_modules = true
use_try_shorthand = true
use_small_heuristics = "Max"
# Unstable features (nightly only)
unstable_features = true

View File

@ -144,7 +144,10 @@ where
if !invalid_unicode_filenames.is_empty() {
let error = FinalError::with_title("Cannot build zip archive")
.detail("Zip archives require files to have valid UTF-8 paths")
.detail(format!("Files with invalid paths: {}", concatenate_os_str_list(&invalid_unicode_filenames)));
.detail(format!(
"Files with invalid paths: {}",
concatenate_os_str_list(&invalid_unicode_filenames)
));
return Err(error.into());
}
@ -223,7 +226,10 @@ fn convert_zip_date_time(date_time: zip::DateTime) -> Option<libc::timespec> {
let date_time = PrimitiveDateTime::new(date, time);
let timestamp = date_time.assume_utc().unix_timestamp();
Some(libc::timespec { tv_sec: timestamp, tv_nsec: 0 })
Some(libc::timespec {
tv_sec: timestamp,
tv_nsec: 0,
})
}
#[cfg(unix)]
@ -232,7 +238,10 @@ fn set_last_modified_time(file: &fs::File, zip_file: &ZipFile) -> crate::Result<
use libc::UTIME_NOW;
let now = libc::timespec { tv_sec: 0, tv_nsec: UTIME_NOW };
let now = libc::timespec {
tv_sec: 0,
tv_nsec: UTIME_NOW,
};
let last_modified = zip_file.last_modified();
let last_modified = convert_zip_date_time(last_modified).unwrap_or(now);

View File

@ -38,7 +38,9 @@ pub fn compress_files(
let (total_input_size, precise) = files
.iter()
.map(|f| (f.metadata().expect("file exists").len(), f.is_file()))
.fold((0, true), |(total_size, and_precise), (size, precise)| (total_size + size, and_precise & precise));
.fold((0, true), |(total_size, and_precise), (size, precise)| {
(total_size + size, and_precise & precise)
});
// NOTE: canonicalize is here to avoid a weird bug:
// > If output_file_path is a nested path and it exists and the user overwrite it
@ -81,7 +83,9 @@ pub fn compress_files(
let _progress = Progress::new_accessible_aware(
total_input_size,
precise,
Some(Box::new(move || output_file_path.metadata().expect("file exists").len())),
Some(Box::new(move || {
output_file_path.metadata().expect("file exists").len()
})),
);
writer = chain_writer_encoder(&first_extension, writer)?;
@ -92,14 +96,19 @@ pub fn compress_files(
let mut progress = Progress::new_accessible_aware(
total_input_size,
precise,
Some(Box::new(move || output_file_path.metadata().expect("file exists").len())),
Some(Box::new(move || {
output_file_path.metadata().expect("file exists").len()
})),
);
archive::tar::build_archive_from_paths(
&files,
&mut writer,
file_visibility_policy,
progress.as_mut().map(Progress::display_handle).unwrap_or(&mut io::stdout()),
progress
.as_mut()
.map(Progress::display_handle)
.unwrap_or(&mut io::stdout()),
)?;
writer.flush()?;
}
@ -134,7 +143,10 @@ pub fn compress_files(
&files,
&mut vec_buffer,
file_visibility_policy,
progress.as_mut().map(Progress::display_handle).unwrap_or(&mut io::stdout()),
progress
.as_mut()
.map(Progress::display_handle)
.unwrap_or(&mut io::stdout()),
)?;
let vec_buffer = vec_buffer.into_inner();
io::copy(&mut vec_buffer.as_slice(), &mut writer)?;

View File

@ -43,7 +43,11 @@ pub fn decompress_file(
// in-memory decompression/copying first.
//
// Any other Zip decompression done can take up the whole RAM and freeze ouch.
if let [Extension { compression_formats: [Zip], .. }] = formats.as_slice() {
if let [Extension {
compression_formats: [Zip],
..
}] = formats.as_slice()
{
let zip_archive = zip::ZipArchive::new(reader)?;
let files = if let ControlFlow::Continue(files) = smart_unpack(
Box::new(move |output_dir| {
@ -51,7 +55,10 @@ pub fn decompress_file(
crate::archive::zip::unpack_archive(
zip_archive,
output_dir,
progress.as_mut().map(Progress::display_handle).unwrap_or(&mut io::stdout()),
progress
.as_mut()
.map(Progress::display_handle)
.unwrap_or(&mut io::stdout()),
)
}),
output_dir,
@ -128,7 +135,10 @@ pub fn decompress_file(
crate::archive::tar::unpack_archive(
reader,
output_dir,
progress.as_mut().map(Progress::display_handle).unwrap_or(&mut io::stdout()),
progress
.as_mut()
.map(Progress::display_handle)
.unwrap_or(&mut io::stdout()),
)
}),
output_dir,
@ -160,7 +170,10 @@ pub fn decompress_file(
crate::archive::zip::unpack_archive(
zip_archive,
output_dir,
progress.as_mut().map(Progress::display_handle).unwrap_or(&mut io::stdout()),
progress
.as_mut()
.map(Progress::display_handle)
.unwrap_or(&mut io::stdout()),
)
}),
output_dir,
@ -178,7 +191,11 @@ pub fn decompress_file(
// having a final status message is important especially in an accessibility context
// as screen readers may not read a commands exit code, making it hard to reason
// about whether the command succeeded without such a message
info!(accessible, "Successfully decompressed archive in {}.", nice_directory_display(output_dir));
info!(
accessible,
"Successfully decompressed archive in {}.",
nice_directory_display(output_dir)
);
info!(accessible, "Files unpacked: {}", files_unpacked.len());
Ok(())
@ -211,8 +228,9 @@ fn smart_unpack(
// Only one file in the root directory, so we can just move it to the output directory
let file = fs::read_dir(&temp_dir_path)?.next().expect("item exists")?;
let file_path = file.path();
let file_name =
file_path.file_name().expect("Should be safe because paths in archives should not end with '..'");
let file_name = file_path
.file_name()
.expect("Should be safe because paths in archives should not end with '..'");
let correct_path = output_dir.join(file_name);
// One case to handle tough is we need to check if a file with the same name already exists
if !utils::clear_path(&correct_path, question_policy)? {

View File

@ -74,7 +74,10 @@ pub fn run(
file_visibility_policy: FileVisibilityPolicy,
) -> crate::Result<()> {
match args.cmd {
Subcommand::Compress { mut files, output: output_path } => {
Subcommand::Compress {
mut files,
output: output_path,
} => {
// If the output_path file exists and is the same as some of the input files, warn the user and skip those inputs (in order to avoid compression recursion)
if output_path.exists() {
deduplicate_input_files(&mut files, &fs::canonicalize(&output_path)?);
@ -95,7 +98,10 @@ pub fn run(
.hint(format!(" ouch compress <FILES>... {}.zip", to_utf(&output_path)))
.hint("")
.hint("Alternatively, you can overwrite this option by using the '--format' flag:")
.hint(format!(" ouch compress <FILES>... {} --format tar.gz", to_utf(&output_path)));
.hint(format!(
" ouch compress <FILES>... {} --format tar.gz",
to_utf(&output_path)
));
return Err(error.into());
}
@ -118,7 +124,10 @@ pub fn run(
let error = FinalError::with_title(format!("Cannot compress to '{}'.", output_path))
.detail("You are trying to compress multiple files.")
.detail(format!("The compression format '{}' cannot receive multiple files.", &formats[0]))
.detail(format!(
"The compression format '{}' cannot receive multiple files.",
&formats[0]
))
.detail("The only supported formats that archive files into an archive are .tar and .zip.")
.hint(format!("Try inserting '.tar' or '.zip' before '{}'.", &formats[0]))
.hint(format!("From: {}", output_path))
@ -130,9 +139,19 @@ pub fn run(
if let Some(format) = formats.iter().skip(1).find(|format| format.is_archive()) {
let error = FinalError::with_title(format!("Cannot compress to '{}'.", to_utf(&output_path)))
.detail(format!("Found the format '{}' in an incorrect position.", format))
.detail(format!("'{}' can only be used at the start of the file extension.", format))
.hint(format!("If you wish to compress multiple files, start the extension with '{}'.", format))
.hint(format!("Otherwise, remove the last '{}' from '{}'.", format, to_utf(&output_path)));
.detail(format!(
"'{}' can only be used at the start of the file extension.",
format
))
.hint(format!(
"If you wish to compress multiple files, start the extension with '{}'.",
format
))
.hint(format!(
"Otherwise, remove the last '{}' from '{}'.",
format,
to_utf(&output_path)
));
return Err(error.into());
}
@ -185,8 +204,14 @@ pub fn run(
formats = new_formats;
}
}
let compress_result =
compress_files(files, formats, output_file, &output_path, question_policy, file_visibility_policy);
let compress_result = compress_files(
files,
formats,
output_file,
&output_path,
question_policy,
file_visibility_policy,
);
if let Ok(true) = compress_result {
// this is only printed once, so it doesn't result in much text. On the other hand,
@ -201,8 +226,16 @@ pub fn run(
if let Err(err) = fs::remove_file(&output_path) {
eprintln!("{red}FATAL ERROR:\n", red = *colors::RED);
eprintln!(" Please manually delete '{}'.", to_utf(&output_path));
eprintln!(" Compression failed and we could not delete '{}'.", to_utf(&output_path),);
eprintln!(" Error:{reset} {}{red}.{reset}\n", err, reset = *colors::RESET, red = *colors::RED);
eprintln!(
" Compression failed and we could not delete '{}'.",
to_utf(&output_path),
);
eprintln!(
" Error:{reset} {}{red}.{reset}\n",
err,
reset = *colors::RESET,
red = *colors::RED
);
}
}
@ -240,7 +273,10 @@ pub fn run(
.hint(" ouch decompress example.tar.gz")
.hint("")
.hint("Or overwrite this option with the '--format' flag:")
.hint(format!(" ouch decompress {} --format tar.gz", to_utf(&files_missing_format[0])));
.hint(format!(
" ouch decompress {} --format tar.gz",
to_utf(&files_missing_format[0])
));
return Err(error.into());
}
@ -285,7 +321,10 @@ pub fn run(
if !not_archives.is_empty() {
let error = FinalError::with_title("Cannot list archive contents")
.detail("Only archives can have their contents listed")
.detail(format!("Files are not archives: {}", concatenate_os_str_list(&not_archives)));
.detail(format!(
"Files are not archives: {}",
concatenate_os_str_list(&not_archives)
));
return Err(error.into());
}
@ -316,7 +355,12 @@ fn check_mime_type(
if let Some(detected_format) = try_infer_extension(path) {
// Infering the file extension can have unpredicted consequences (e.g. the user just
// mistyped, ...) which we should always inform the user about.
info!(accessible, "Detected file: `{}` extension as `{}`", path.display(), detected_format);
info!(
accessible,
"Detected file: `{}` extension as `{}`",
path.display(),
detected_format
);
if user_wants_to_continue(path, question_policy, QuestionAction::Decompression)? {
format.push(detected_format);
} else {
@ -350,7 +394,10 @@ fn deduplicate_input_files(files: &mut Vec<PathBuf>, output_path: &Path) {
let mut idx = 0;
while idx < files.len() {
if files[idx] == output_path {
warning!("The output file and the input file are the same: `{}`, skipping...", output_path.display());
warning!(
"The output file and the input file are the same: `{}`, skipping...",
output_path.display()
);
files.remove(idx);
} else {
idx += 1;

View File

@ -94,7 +94,11 @@ impl FinalError {
/// Only constructor
#[must_use]
pub fn with_title(title: impl Into<CowStr>) -> Self {
Self { title: title.into(), details: vec![], hints: vec![] }
Self {
title: title.into(),
details: vec![],
hints: vec![],
}
}
/// Add one detail line, can have multiple
@ -142,17 +146,35 @@ impl fmt::Display for Error {
impl From<std::io::Error> for Error {
fn from(err: std::io::Error) -> Self {
match err.kind() {
std::io::ErrorKind::NotFound => Self::NotFound { error_title: err.to_string() },
std::io::ErrorKind::PermissionDenied => Self::PermissionDenied { error_title: err.to_string() },
std::io::ErrorKind::AlreadyExists => Self::AlreadyExists { error_title: err.to_string() },
_other => Self::IoError { reason: err.to_string() },
std::io::ErrorKind::NotFound => {
Self::NotFound {
error_title: err.to_string(),
}
}
std::io::ErrorKind::PermissionDenied => {
Self::PermissionDenied {
error_title: err.to_string(),
}
}
std::io::ErrorKind::AlreadyExists => {
Self::AlreadyExists {
error_title: err.to_string(),
}
}
_other => {
Self::IoError {
reason: err.to_string(),
}
}
}
}
}
impl From<lzzzz::lz4f::Error> for Error {
fn from(err: lzzzz::lz4f::Error) -> Self {
Self::Lz4Error { reason: err.to_string() }
Self::Lz4Error {
reason: err.to_string(),
}
}
}
@ -174,7 +196,9 @@ impl From<zip::result::ZipError> for Error {
impl From<ignore::Error> for Error {
fn from(err: ignore::Error) -> Self {
Self::WalkdirError { reason: err.to_string() }
Self::WalkdirError {
reason: err.to_string(),
}
}
}

View File

@ -25,7 +25,10 @@ impl Extension {
/// Will panic if `formats` is empty
pub fn new(formats: &'static [CompressionFormat], text: impl ToString) -> Self {
assert!(!formats.is_empty());
Self { compression_formats: formats, display_text: text.to_string() }
Self {
compression_formats: formats,
display_text: text.to_string(),
}
}
/// Checks if the first format in `compression_formats` is an archive
@ -80,20 +83,18 @@ impl CompressionFormat {
impl fmt::Display for CompressionFormat {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{}",
match self {
Gzip => ".gz",
Bzip => ".bz",
Zstd => ".zst",
Lz4 => ".lz4",
Lzma => ".lz",
Snappy => ".sz",
Tar => ".tar",
Zip => ".zip",
}
)
let text = match self {
Gzip => ".gz",
Bzip => ".bz",
Zstd => ".zst",
Lz4 => ".lz4",
Lzma => ".lz",
Snappy => ".sz",
Tar => ".tar",
Zip => ".zip",
};
write!(f, "{text}")
}
}
@ -137,7 +138,11 @@ pub fn separate_known_extensions_from_name(mut path: &Path) -> (&Path, Vec<Exten
extensions.push(extension);
// Update for the next iteration
path = if let Some(stem) = path.file_stem() { Path::new(stem) } else { Path::new("") };
path = if let Some(stem) = path.file_stem() {
Path::new(stem)
} else {
Path::new("")
};
}
// Put the extensions in the correct order: left to right
extensions.reverse();
@ -175,5 +180,9 @@ pub fn split_first_compression_format(formats: &[Extension]) -> (CompressionForm
}
pub fn flatten_compression_formats(extensions: &[Extension]) -> Vec<CompressionFormat> {
extensions.iter().flat_map(|extension| extension.compression_formats.iter()).copied().collect()
extensions
.iter()
.flat_map(|extension| extension.compression_formats.iter())
.copied()
.collect()
}

View File

@ -47,7 +47,9 @@ pub fn list_files(
.map(|file| {
let file = file?;
if !crate::cli::ACCESSIBLE.get().unwrap() {
pb.as_ref().expect("exists").set_message(format!("Processing: {}", file.path.display()));
pb.as_ref()
.expect("exists")
.set_message(format!("Processing: {}", file.path.display()));
}
Ok(file)
})

View File

@ -36,7 +36,9 @@ impl io::Write for DisplayHandle {
fn io_error<X>(_: X) -> io::Error {
io::Error::new(io::ErrorKind::Other, "failed to flush buffer")
}
self.sender.send(String::from_utf8(self.buf.drain(..).collect()).map_err(io_error)?).map_err(io_error)
self.sender
.send(String::from_utf8(self.buf.drain(..).collect()).map_err(io_error)?)
.map_err(io_error)
}
}
@ -99,7 +101,10 @@ impl Progress {
Progress {
draw_stop: draw_tx,
clean_done: clean_rx,
display_handle: DisplayHandle { buf: Vec::new(), sender: msg_tx },
display_handle: DisplayHandle {
buf: Vec::new(),
sender: msg_tx,
},
}
}

View File

@ -23,7 +23,12 @@ pub struct FileVisibilityPolicy {
impl Default for FileVisibilityPolicy {
fn default() -> Self {
Self { read_ignore: false, read_hidden: true, read_git_ignore: false, read_git_exclude: false }
Self {
read_ignore: false,
read_hidden: true,
read_git_ignore: false,
read_git_exclude: false,
}
}
}
@ -41,13 +46,19 @@ impl FileVisibilityPolicy {
#[must_use]
/// Enables reading .gitignore files.
pub fn read_git_ignore(self, read_git_ignore: bool) -> Self {
Self { read_git_ignore, ..self }
Self {
read_git_ignore,
..self
}
}
#[must_use]
/// Enables reading `.git/info/exclude` files.
pub fn read_git_exclude(self, read_git_exclude: bool) -> Self {
Self { read_git_exclude, ..self }
Self {
read_git_exclude,
..self
}
}
#[must_use]

View File

@ -129,5 +129,7 @@ pub fn try_infer_extension(path: &Path) -> Option<Extension> {
/// This is the same as the nightly https://doc.rust-lang.org/std/path/struct.Path.html#method.is_symlink
// Useful to detect broken symlinks when compressing. (So we can safely ignore them)
pub fn is_symlink(path: &Path) -> bool {
fs::symlink_metadata(path).map(|m| m.file_type().is_symlink()).unwrap_or(false)
fs::symlink_metadata(path)
.map(|m| m.file_type().is_symlink())
.unwrap_or(false)
}

View File

@ -29,6 +29,9 @@ mod utf8 {
/// Filter out list of paths that are not utf8 valid
pub fn get_invalid_utf8_paths(paths: &[PathBuf]) -> Vec<&PathBuf> {
paths.iter().filter_map(|path| is_invalid_utf8(path).then(|| path)).collect()
paths
.iter()
.filter_map(|path| is_invalid_utf8(path).then(|| path))
.collect()
}
}

View File

@ -110,7 +110,10 @@ pub struct Confirmation<'a> {
impl<'a> Confirmation<'a> {
/// Creates a new Confirmation.
pub const fn new(prompt: &'a str, pattern: Option<&'a str>) -> Self {
Self { prompt, placeholder: pattern }
Self {
prompt,
placeholder: pattern,
}
}
/// Creates user message and receives a boolean input to be used on the program
@ -124,9 +127,23 @@ impl<'a> Confirmation<'a> {
// Ask the same question to end while no valid answers are given
loop {
if *crate::cli::ACCESSIBLE.get().unwrap() {
print!("{} {}yes{}/{}no{}: ", message, *colors::GREEN, *colors::RESET, *colors::RED, *colors::RESET);
print!(
"{} {}yes{}/{}no{}: ",
message,
*colors::GREEN,
*colors::RESET,
*colors::RED,
*colors::RESET
);
} else {
print!("{} [{}Y{}/{}n{}] ", message, *colors::GREEN, *colors::RESET, *colors::RED, *colors::RESET);
print!(
"{} [{}Y{}/{}n{}] ",
message,
*colors::GREEN,
*colors::RESET,
*colors::RED,
*colors::RESET
);
}
io::stdout().flush()?;

View File

@ -51,7 +51,10 @@ enum Extension {
// converts a list of extension structs to string
fn merge_extensions(ext: impl ToString, exts: Vec<FileExtension>) -> String {
once(ext.to_string()).chain(exts.into_iter().map(|x| x.to_string())).collect::<Vec<_>>().join(".")
once(ext.to_string())
.chain(exts.into_iter().map(|x| x.to_string()))
.collect::<Vec<_>>()
.join(".")
}
// create random nested directories and files under the specified directory
@ -64,7 +67,10 @@ fn create_random_files(dir: impl Into<PathBuf>, depth: u8, rng: &mut SmallRng) {
// create 0 to 7 random files
for _ in 0..rng.gen_range(0..8u32) {
write_random_content(&mut tempfile::Builder::new().tempfile_in(dir).unwrap().keep().unwrap().0, rng);
write_random_content(
&mut tempfile::Builder::new().tempfile_in(dir).unwrap().keep().unwrap().0,
rng,
);
}
// create more random files in 0 to 3 new directories
@ -83,7 +89,10 @@ fn single_empty_file(ext: Extension, #[any(size_range(0..8).lift())] exts: Vec<F
let before_file = &before.join("file");
let archive = &dir.join(format!("file.{}", merge_extensions(ext, exts)));
let after = &dir.join("after");
write_random_content(&mut fs::File::create(before_file).unwrap(), &mut SmallRng::from_entropy());
write_random_content(
&mut fs::File::create(before_file).unwrap(),
&mut SmallRng::from_entropy(),
);
ouch!("-A", "c", before_file, archive);
ouch!("-A", "d", archive, "-d", after);
assert_same_directory(before, after, false);

View File

@ -46,8 +46,9 @@ fn sanity_check_through_mime() {
let compressed_file_path = &format!("{}.{}", path_to_compress.display(), format);
ouch!("c", path_to_compress, compressed_file_path);
let sniffed =
infer::get_from_path(compressed_file_path).expect("the file to be read").expect("the MIME to be found");
let sniffed = infer::get_from_path(compressed_file_path)
.expect("the file to be read")
.expect("the MIME to be found");
assert_eq!(&sniffed.mime_type(), expected_mime);
}