mirror of
https://github.com/ouch-org/ouch.git
synced 2025-06-05 02:55:31 +00:00
remove use_small_heuristics = "Max"
from rustfmt
and update edition to 2021
This commit is contained in:
parent
f5fcf7f2a3
commit
14025c6816
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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)?;
|
||||
|
@ -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)? {
|
||||
|
@ -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(¬_archives)));
|
||||
.detail(format!(
|
||||
"Files are not archives: {}",
|
||||
concatenate_os_str_list(¬_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;
|
||||
|
38
src/error.rs
38
src/error.rs
@ -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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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)
|
||||
})
|
||||
|
@ -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,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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]
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
@ -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()?;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user