Resize options added
This commit is contained in:
parent
559e2a48a6
commit
7e0efdb5b7
|
@ -0,0 +1,5 @@
|
||||||
|
<component name="ProjectCodeStyleConfiguration">
|
||||||
|
<state>
|
||||||
|
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
|
||||||
|
</state>
|
||||||
|
</component>
|
|
@ -199,9 +199,12 @@ version = "1.0.0-beta.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
"filetime",
|
"filetime",
|
||||||
|
"human_bytes",
|
||||||
"image",
|
"image",
|
||||||
|
"imagesize",
|
||||||
"indicatif",
|
"indicatif",
|
||||||
"infer",
|
"infer",
|
||||||
|
"kamadak-exif 0.6.1",
|
||||||
"libcaesium",
|
"libcaesium",
|
||||||
"rayon",
|
"rayon",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
|
@ -527,6 +530,12 @@ version = "0.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "human_bytes"
|
||||||
|
version = "0.4.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "91f255a4535024abf7640cb288260811fc14794f62b063652ed349f9a6c2348e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "image"
|
name = "image"
|
||||||
version = "0.25.5"
|
version = "0.25.5"
|
||||||
|
@ -573,6 +582,12 @@ dependencies = [
|
||||||
"thread_local",
|
"thread_local",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "imagesize"
|
||||||
|
version = "0.13.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "edcd27d72f2f071c64249075f42e205ff93c9a4c5f6c6da53e79ed9f9832c285"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "img-parts"
|
name = "img-parts"
|
||||||
version = "0.3.1"
|
version = "0.3.1"
|
||||||
|
@ -683,6 +698,15 @@ dependencies = [
|
||||||
"mutate_once",
|
"mutate_once",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "kamadak-exif"
|
||||||
|
version = "0.6.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1130d80c7374efad55a117d715a3af9368f0fa7a2c54573afc15a188cd984837"
|
||||||
|
dependencies = [
|
||||||
|
"mutate_once",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lazy_static"
|
name = "lazy_static"
|
||||||
version = "1.5.0"
|
version = "1.5.0"
|
||||||
|
@ -713,7 +737,7 @@ dependencies = [
|
||||||
"imagequant",
|
"imagequant",
|
||||||
"img-parts",
|
"img-parts",
|
||||||
"infer",
|
"infer",
|
||||||
"kamadak-exif",
|
"kamadak-exif 0.5.5",
|
||||||
"libc",
|
"libc",
|
||||||
"lodepng",
|
"lodepng",
|
||||||
"mozjpeg-sys",
|
"mozjpeg-sys",
|
||||||
|
|
|
@ -11,9 +11,10 @@ walkdir = "2.5"
|
||||||
infer = "0.16"
|
infer = "0.16"
|
||||||
rayon = "1.10"
|
rayon = "1.10"
|
||||||
#rand = "0.8"
|
#rand = "0.8"
|
||||||
#human_bytes = { version = "0.4", default-features = false }
|
human_bytes = { version = "0.4", default-features = false }
|
||||||
|
kamadak-exif = "0.6"
|
||||||
filetime = "0.2"
|
filetime = "0.2"
|
||||||
#imagesize = "0.13"
|
imagesize = "0.13"
|
||||||
libcaesium = "0.17.0"
|
libcaesium = "0.17.0"
|
||||||
clap = { version = "4.5", features = ["derive"] }
|
clap = { version = "4.5", features = ["derive"] }
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
comment_width = 120
|
||||||
|
format_code_in_doc_comments = true
|
||||||
|
imports_granularity = "Crate"
|
||||||
|
imports_layout = "Vertical"
|
||||||
|
wrap_comments = true
|
||||||
|
max_width = 120
|
174
src/main.rs
174
src/main.rs
|
@ -1,5 +1,5 @@
|
||||||
use crate::options::{CommandLineArgs, OverwritePolicy};
|
use crate::options::{CommandLineArgs, OverwritePolicy};
|
||||||
use crate::scan_files::scan_files;
|
use crate::scan_files::{get_file_mime_type, scan_files};
|
||||||
use caesium::compress_in_memory;
|
use caesium::compress_in_memory;
|
||||||
use caesium::parameters::CSParameters;
|
use caesium::parameters::CSParameters;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
|
@ -7,16 +7,18 @@ use filetime::{set_file_times, FileTime};
|
||||||
use indicatif::{ParallelProgressIterator, ProgressBar, ProgressDrawTarget, ProgressStyle};
|
use indicatif::{ParallelProgressIterator, ProgressBar, ProgressDrawTarget, ProgressStyle};
|
||||||
use rayon::iter::IntoParallelRefIterator;
|
use rayon::iter::IntoParallelRefIterator;
|
||||||
use rayon::iter::ParallelIterator;
|
use rayon::iter::ParallelIterator;
|
||||||
|
use std::error::Error;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{Read, Write};
|
use std::io::{BufReader, Read, Write};
|
||||||
use std::num::NonZero;
|
use std::num::NonZero;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use std::{fs, io};
|
use std::{fs, io};
|
||||||
|
use human_bytes::human_bytes;
|
||||||
|
|
||||||
|
mod logger;
|
||||||
mod options;
|
mod options;
|
||||||
mod scan_files;
|
mod scan_files;
|
||||||
mod logger;
|
|
||||||
|
|
||||||
enum CompressionStatus {
|
enum CompressionStatus {
|
||||||
Success,
|
Success,
|
||||||
|
@ -44,8 +46,11 @@ fn main() {
|
||||||
.get(),
|
.get(),
|
||||||
);
|
);
|
||||||
let verbose = if quiet { 0 } else { args.verbose };
|
let verbose = if quiet { 0 } else { args.verbose };
|
||||||
let compression_parameters = build_compression_parameters(&args);
|
let needs_resize = args.resize.width.is_some()
|
||||||
let (base_path, input_files) = scan_files(args.files, args.recursive, quiet);
|
|| args.resize.height.is_some()
|
||||||
|
|| args.resize.long_edge.is_some()
|
||||||
|
|| args.resize.short_edge.is_some();
|
||||||
|
let (base_path, input_files) = scan_files(&args.files, args.recursive, quiet);
|
||||||
|
|
||||||
rayon::ThreadPoolBuilder::new()
|
rayon::ThreadPoolBuilder::new()
|
||||||
.num_threads(threads_number)
|
.num_threads(threads_number)
|
||||||
|
@ -68,14 +73,14 @@ fn main() {
|
||||||
message: String::new(),
|
message: String::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let original_file_size = match input_file.metadata() {
|
let input_file_metadata = match input_file.metadata() {
|
||||||
Ok(m) => m.len(),
|
Ok(m) => m,
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
compression_result.message = "Error reading file metadata".to_string();
|
compression_result.message = "Error reading file metadata".to_string();
|
||||||
return compression_result;
|
return compression_result;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
let original_file_size = input_file_metadata.len();
|
||||||
compression_result.original_size = original_file_size;
|
compression_result.original_size = original_file_size;
|
||||||
|
|
||||||
let output_directory = if args.output_destination.same_folder_as_input {
|
let output_directory = if args.output_destination.same_folder_as_input {
|
||||||
|
@ -115,16 +120,17 @@ fn main() {
|
||||||
return compression_result;
|
return compression_result;
|
||||||
};
|
};
|
||||||
|
|
||||||
let compressed_image = match compress_in_memory(
|
let compression_parameters = build_compression_parameters(&args, input_file, needs_resize);
|
||||||
read_file_to_vec(input_file).unwrap(),
|
|
||||||
&compression_parameters,
|
let compressed_image =
|
||||||
) {
|
match compress_in_memory(read_file_to_vec(input_file).unwrap(), &compression_parameters) {
|
||||||
Ok(v) => v,
|
//TODO: handle error
|
||||||
Err(e) => {
|
Ok(v) => v,
|
||||||
compression_result.message = format!("Error compressing file: {}", e);
|
Err(e) => {
|
||||||
return compression_result;
|
compression_result.message = format!("Error compressing file: {}", e);
|
||||||
}
|
return compression_result;
|
||||||
};
|
}
|
||||||
|
};
|
||||||
compression_result.output_path = output_full_path.display().to_string();
|
compression_result.output_path = output_full_path.display().to_string();
|
||||||
let output_file_size = compressed_image.len() as u64;
|
let output_file_size = compressed_image.len() as u64;
|
||||||
|
|
||||||
|
@ -133,8 +139,7 @@ fn main() {
|
||||||
OverwritePolicy::None => {
|
OverwritePolicy::None => {
|
||||||
compression_result.status = CompressionStatus::Skipped;
|
compression_result.status = CompressionStatus::Skipped;
|
||||||
compression_result.compressed_size = original_file_size;
|
compression_result.compressed_size = original_file_size;
|
||||||
compression_result.message =
|
compression_result.message = "File already exists, skipped due overwrite policy".to_string();
|
||||||
"File already exists, skipped due overwrite policy".to_string();
|
|
||||||
return compression_result;
|
return compression_result;
|
||||||
}
|
}
|
||||||
OverwritePolicy::Bigger => {
|
OverwritePolicy::Bigger => {
|
||||||
|
@ -166,17 +171,9 @@ fn main() {
|
||||||
};
|
};
|
||||||
|
|
||||||
if args.keep_dates {
|
if args.keep_dates {
|
||||||
let output_file_metadata = match output_file.metadata() {
|
|
||||||
Ok(m) => m,
|
|
||||||
Err(_) => {
|
|
||||||
compression_result.message =
|
|
||||||
"Error reading output file metadata".to_string();
|
|
||||||
return compression_result;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let (last_modification_time, last_access_time) = (
|
let (last_modification_time, last_access_time) = (
|
||||||
FileTime::from_last_modification_time(&output_file_metadata),
|
FileTime::from_last_modification_time(&input_file_metadata),
|
||||||
FileTime::from_last_access_time(&output_file_metadata),
|
FileTime::from_last_access_time(&input_file_metadata),
|
||||||
);
|
);
|
||||||
match preserve_dates(&output_full_path, last_modification_time, last_access_time) {
|
match preserve_dates(&output_full_path, last_modification_time, last_access_time) {
|
||||||
Ok(_) => {}
|
Ok(_) => {}
|
||||||
|
@ -192,8 +189,40 @@ fn main() {
|
||||||
compression_result
|
compression_result
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
write_recap_message(&compression_results, verbose);
|
||||||
|
}
|
||||||
|
|
||||||
let recap_message = format!("Processed {} files", compression_results.len());
|
fn write_recap_message(compression_results: &[CompressionResult], verbose: u8) {
|
||||||
|
let mut total_original_size = 0;
|
||||||
|
let mut total_compressed_size = 0;
|
||||||
|
let total_files = compression_results.len();
|
||||||
|
let mut total_success = 0;
|
||||||
|
let mut total_skipped = 0;
|
||||||
|
let mut total_errors = 0;
|
||||||
|
|
||||||
|
for result in compression_results.iter() {
|
||||||
|
total_original_size += result.original_size;
|
||||||
|
total_compressed_size += result.compressed_size;
|
||||||
|
match result.status {
|
||||||
|
CompressionStatus::Skipped => total_skipped += 1,
|
||||||
|
CompressionStatus::Error => total_errors += 1,
|
||||||
|
_ => total_success += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let total_saved = total_original_size as f64 - total_compressed_size as f64;
|
||||||
|
let total_saved_percent = total_saved / total_original_size as f64 * 100.0;
|
||||||
|
|
||||||
|
if verbose > 0 {
|
||||||
|
println!("Total files: {}", total_files);
|
||||||
|
println!("Total success: {}", total_success);
|
||||||
|
println!("Total skipped: {}", total_skipped);
|
||||||
|
println!("Total errors: {}", total_errors);
|
||||||
|
println!("Total original size: {}", human_bytes(total_original_size as f64));
|
||||||
|
println!("Total compressed size: {}", human_bytes(total_compressed_size as f64));
|
||||||
|
println!("Total saved: {:.2} bytes ({:.2}%)", human_bytes(total_saved), total_saved_percent);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_parallelism_count(requested_threads: u32, available_threads: usize) -> usize {
|
fn get_parallelism_count(requested_threads: u32, available_threads: usize) -> usize {
|
||||||
|
@ -204,7 +233,7 @@ fn get_parallelism_count(requested_threads: u32, available_threads: usize) -> us
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_compression_parameters(args: &CommandLineArgs) -> CSParameters {
|
fn build_compression_parameters(args: &CommandLineArgs, input_file: &Path, needs_resize: bool) -> CSParameters {
|
||||||
let mut parameters = CSParameters::new();
|
let mut parameters = CSParameters::new();
|
||||||
let quality = args.compression.quality.unwrap_or(80) as u32;
|
let quality = args.compression.quality.unwrap_or(80) as u32;
|
||||||
|
|
||||||
|
@ -218,9 +247,46 @@ fn build_compression_parameters(args: &CommandLineArgs) -> CSParameters {
|
||||||
parameters.png.optimization_level = args.png_opt_level;
|
parameters.png.optimization_level = args.png_opt_level;
|
||||||
parameters.png.force_zopfli = args.zopfli;
|
parameters.png.force_zopfli = args.zopfli;
|
||||||
|
|
||||||
|
if needs_resize {
|
||||||
|
let mime_type = get_file_mime_type(input_file);
|
||||||
|
build_resize_parameters(args, &mut parameters, input_file, mime_type).unwrap();
|
||||||
|
//TODO
|
||||||
|
}
|
||||||
|
|
||||||
parameters
|
parameters
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn build_resize_parameters(
|
||||||
|
args: &CommandLineArgs,
|
||||||
|
parameters: &mut CSParameters,
|
||||||
|
input_file_path: &Path,
|
||||||
|
mime_type: Option<String>,
|
||||||
|
) -> Result<(), Box<dyn Error>> {
|
||||||
|
let (width, height) = get_real_resolution(input_file_path, mime_type, args.exif)?;
|
||||||
|
|
||||||
|
if args.resize.width.is_some() {
|
||||||
|
parameters.width = args.resize.width.unwrap_or(0);
|
||||||
|
} else if args.resize.height.is_some() {
|
||||||
|
parameters.height = args.resize.height.unwrap_or(0);
|
||||||
|
} else if args.resize.long_edge.is_some() {
|
||||||
|
let long_edge = args.resize.long_edge.unwrap_or(0);
|
||||||
|
if width > height {
|
||||||
|
parameters.width = long_edge;
|
||||||
|
} else {
|
||||||
|
parameters.height = long_edge;
|
||||||
|
}
|
||||||
|
} else if args.resize.short_edge.is_some() {
|
||||||
|
let short_edge = args.resize.short_edge.unwrap_or(0);
|
||||||
|
if width < height {
|
||||||
|
parameters.width = short_edge;
|
||||||
|
} else {
|
||||||
|
parameters.height = short_edge;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn compute_output_full_path(
|
fn compute_output_full_path(
|
||||||
output_directory: PathBuf,
|
output_directory: PathBuf,
|
||||||
input_file_path: PathBuf,
|
input_file_path: PathBuf,
|
||||||
|
@ -228,14 +294,8 @@ fn compute_output_full_path(
|
||||||
keep_structure: bool,
|
keep_structure: bool,
|
||||||
suffix: &str,
|
suffix: &str,
|
||||||
) -> Option<PathBuf> {
|
) -> Option<PathBuf> {
|
||||||
let extension = input_file_path
|
let extension = input_file_path.extension().unwrap_or_default().to_os_string();
|
||||||
.extension()
|
let base_name = input_file_path.file_stem().unwrap_or_default().to_os_string();
|
||||||
.unwrap_or_default()
|
|
||||||
.to_os_string();
|
|
||||||
let base_name = input_file_path
|
|
||||||
.file_stem()
|
|
||||||
.unwrap_or_default()
|
|
||||||
.to_os_string();
|
|
||||||
let mut output_file_name = base_name;
|
let mut output_file_name = base_name;
|
||||||
output_file_name.push(suffix);
|
output_file_name.push(suffix);
|
||||||
if !extension.is_empty() {
|
if !extension.is_empty() {
|
||||||
|
@ -269,14 +329,36 @@ fn read_file_to_vec(file_path: &PathBuf) -> io::Result<Vec<u8>> {
|
||||||
Ok(buffer)
|
Ok(buffer)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn preserve_dates(
|
fn preserve_dates(output_file: &PathBuf, input_atime: FileTime, input_mtime: FileTime) -> io::Result<()> {
|
||||||
output_file: &PathBuf,
|
|
||||||
input_atime: FileTime,
|
|
||||||
input_mtime: FileTime,
|
|
||||||
) -> io::Result<()> {
|
|
||||||
set_file_times(output_file, input_atime, input_mtime)
|
set_file_times(output_file, input_atime, input_mtime)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_real_resolution(
|
||||||
|
file: &Path,
|
||||||
|
mime_type: Option<String>,
|
||||||
|
keep_metadata: bool,
|
||||||
|
) -> Result<(usize, usize), Box<dyn Error>> {
|
||||||
|
let resolution = imagesize::size(file)?;
|
||||||
|
let mut orientation = 1;
|
||||||
|
let mime = mime_type.unwrap_or("".to_string());
|
||||||
|
if mime == "image/jpeg" && keep_metadata {
|
||||||
|
let f = File::open(file)?;
|
||||||
|
if let Ok(e) = exif::Reader::new().read_from_container(&mut BufReader::new(&f)) {
|
||||||
|
let exif_field = match e.get_field(exif::Tag::Orientation, exif::In::PRIMARY) {
|
||||||
|
Some(f) => f,
|
||||||
|
None => return Ok((resolution.width, resolution.height)),
|
||||||
|
};
|
||||||
|
orientation = exif_field.value.get_uint(0).unwrap_or(1);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
let (width, height) = match orientation {
|
||||||
|
5..=8 => (resolution.height, resolution.width),
|
||||||
|
_ => (resolution.width, resolution.height),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok((width, height))
|
||||||
|
}
|
||||||
|
|
||||||
fn setup_progress_bar(len: usize, verbose: u8) -> ProgressBar {
|
fn setup_progress_bar(len: usize, verbose: u8) -> ProgressBar {
|
||||||
let progress_bar = ProgressBar::new(len as u64);
|
let progress_bar = ProgressBar::new(len as u64);
|
||||||
if verbose == 0 {
|
if verbose == 0 {
|
||||||
|
@ -294,7 +376,7 @@ fn setup_progress_bar(len: usize, verbose: u8) -> ProgressBar {
|
||||||
progress_bar
|
progress_bar
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
|
@ -16,6 +16,9 @@ pub enum OverwritePolicy {
|
||||||
pub struct CommandLineArgs {
|
pub struct CommandLineArgs {
|
||||||
#[command(flatten)]
|
#[command(flatten)]
|
||||||
pub compression: Compression,
|
pub compression: Compression,
|
||||||
|
|
||||||
|
#[command(flatten)]
|
||||||
|
pub resize: Resize,
|
||||||
|
|
||||||
#[command(flatten)]
|
#[command(flatten)]
|
||||||
pub output_destination: OutputDestination,
|
pub output_destination: OutputDestination,
|
||||||
|
@ -87,6 +90,26 @@ pub struct Compression {
|
||||||
pub max_size: Option<u8>,
|
pub max_size: Option<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Args, Debug)]
|
||||||
|
#[group(required = false, multiple = true)]
|
||||||
|
pub struct Resize {
|
||||||
|
/// width of the output image, if height is not set will preserve aspect ratio
|
||||||
|
#[arg(long, conflicts_with_all = &["long_edge", "short_edge"])]
|
||||||
|
pub width: Option<u32>,
|
||||||
|
|
||||||
|
/// height of the output image, if width is not set will preserve aspect ratio
|
||||||
|
#[arg(long, conflicts_with_all = &["long_edge", "short_edge"])]
|
||||||
|
pub height: Option<u32>,
|
||||||
|
|
||||||
|
/// sets the size of the longest edge of the image
|
||||||
|
#[arg(long, conflicts_with_all = &["width", "height", "short_edge"])]
|
||||||
|
pub long_edge: Option<u32>,
|
||||||
|
|
||||||
|
/// sets the size of the shortest edge of the image
|
||||||
|
#[arg(long, conflicts_with_all = &["width", "height", "long_edge"])]
|
||||||
|
pub short_edge: Option<u32>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Args, Debug)]
|
#[derive(Args, Debug)]
|
||||||
#[group(required = true, multiple = false)]
|
#[group(required = true, multiple = false)]
|
||||||
pub struct OutputDestination {
|
pub struct OutputDestination {
|
||||||
|
|
|
@ -6,15 +6,21 @@ use indicatif::ProgressStyle;
|
||||||
use walkdir::WalkDir;
|
use walkdir::WalkDir;
|
||||||
|
|
||||||
fn is_filetype_supported(path: &Path) -> bool {
|
fn is_filetype_supported(path: &Path) -> bool {
|
||||||
|
match get_file_mime_type(path) {
|
||||||
|
Some(mime_type) => {
|
||||||
|
matches!(mime_type.as_str(), "image/jpeg" | "image/png" | "image/webp" | "image/gif")
|
||||||
|
}
|
||||||
|
None => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_file_mime_type(path: &Path) -> Option<String> {
|
||||||
match infer::get_from_path(path) {
|
match infer::get_from_path(path) {
|
||||||
Ok(v) => match v {
|
Ok(v) => match v {
|
||||||
None => false,
|
None => None,
|
||||||
Some(ft) => matches!(
|
Some(ft) => Some(ft.mime_type().to_string()),
|
||||||
ft.mime_type(),
|
|
||||||
"image/jpeg" | "image/png" | "image/gif" | "image/webp"
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
Err(_) => false,
|
Err(_) => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,7 +28,7 @@ fn is_valid(entry: &Path) -> bool {
|
||||||
entry.exists() && entry.is_file() && is_filetype_supported(entry)
|
entry.exists() && entry.is_file() && is_filetype_supported(entry)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn scan_files(args: Vec<String>, recursive: bool, quiet: bool) -> (PathBuf, Vec<PathBuf>) {
|
pub fn scan_files(args: &Vec<String>, recursive: bool, quiet: bool) -> (PathBuf, Vec<PathBuf>) {
|
||||||
if args.is_empty() {
|
if args.is_empty() {
|
||||||
return (PathBuf::new(), vec![]);
|
return (PathBuf::new(), vec![]);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue