1
0
Fork 0
mirror of synced 2024-06-01 18:19:46 +12:00

Changed to StructOpt for CLI argument parser.

This commit is contained in:
Meir Klemfner 2020-10-04 01:00:04 +03:00
parent d9bfb412b9
commit 85a0ec761f
5 changed files with 330 additions and 374 deletions

87
Cargo.lock generated
View file

@ -6,6 +6,15 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234"
[[package]]
name = "ansi_term"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
dependencies = [
"winapi",
]
[[package]]
name = "anyhow"
version = "1.0.32"
@ -59,6 +68,17 @@ dependencies = [
"system-deps",
]
[[package]]
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
"hermit-abi",
"libc",
"winapi",
]
[[package]]
name = "autocfg"
version = "0.1.7"
@ -199,6 +219,21 @@ dependencies = [
"winapi",
]
[[package]]
name = "clap"
version = "2.33.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002"
dependencies = [
"ansi_term",
"atty",
"bitflags",
"strsim",
"textwrap",
"unicode-width",
"vec_map",
]
[[package]]
name = "cloudabi"
version = "0.0.3"
@ -301,6 +336,7 @@ name = "czkawka_cli"
version = "1.0.0"
dependencies = [
"czkawka_core",
"structopt",
]
[[package]]
@ -1827,6 +1863,36 @@ version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0"
[[package]]
name = "strsim"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
[[package]]
name = "structopt"
version = "0.3.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a33f6461027d7f08a13715659b2948e1602c31a3756aeae9378bfe7518c72e82"
dependencies = [
"clap",
"lazy_static",
"structopt-derive",
]
[[package]]
name = "structopt-derive"
version = "0.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c92e775028122a4b3dd55d58f14fc5120289c69bee99df1d117ae30f84b225c9"
dependencies = [
"heck",
"proc-macro-error",
"proc-macro2 1.0.24",
"quote 1.0.7",
"syn 1.0.42",
]
[[package]]
name = "strum"
version = "0.18.0"
@ -1908,6 +1974,15 @@ dependencies = [
"winapi",
]
[[package]]
name = "textwrap"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
dependencies = [
"unicode-width",
]
[[package]]
name = "thiserror"
version = "1.0.20"
@ -1977,6 +2052,12 @@ version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0"
[[package]]
name = "unicode-width"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
[[package]]
name = "unicode-xid"
version = "0.1.0"
@ -1989,6 +2070,12 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
[[package]]
name = "vec_map"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
[[package]]
name = "version-compare"
version = "0.0.10"

View file

@ -9,4 +9,5 @@ homepage = "https://github.com/qarmin/czkawka"
repository = "https://github.com/qarmin/czkawka"
[dependencies]
czkawka_core = { path = "../czkawka_core" }
czkawka_core = { path = "../czkawka_core" }
structopt = "0.3.18"

View file

@ -1,32 +0,0 @@
use std::process;
pub struct ArgumentsPair {
pub command: String,
pub argument: Option<String>,
}
impl ArgumentsPair {
pub fn has_command(ar: &[ArgumentsPair], command: &str) -> bool {
for a in ar {
if a.command == command {
return true;
}
}
false
}
pub fn get_argument(ar: &[ArgumentsPair], command: &str, can_be_empty: bool) -> String {
for a in ar {
if a.command == command {
if !can_be_empty && a.argument == Option::None {
println!("FATAL ERROR: {} commands should have argument passed", command);
process::exit(1);
}
return match &a.argument {
Some(t) => t.clone(),
None => "".to_string(),
};
}
}
panic!("INTERNAL ERROR: Get argument should always return value");
}
}

140
czkawka_cli/src/commands.rs Normal file
View file

@ -0,0 +1,140 @@
use czkawka_core::duplicate::{CheckingMethod, DeleteMethod};
use std::{path::PathBuf, process};
use structopt::StructOpt;
#[derive(Debug, StructOpt)]
#[structopt(name = "czkawka")]
pub enum Commands {
#[structopt(name = "dup", about = "Finds duplicate files")]
Duplicates {
#[structopt(flatten)]
directories: Directories,
#[structopt(flatten)]
excluded_directories: ExludedDirectories,
#[structopt(flatten)]
excluded_items: ExludedItems,
#[structopt(short, long, default_value = "1024", help = "Minimum size in bytes", long_help = "Minimum size of checked files in bytes, assigning bigger value may speed up searching")]
min_size: u64,
#[structopt(flatten)]
allowed_extensions: AllowedExtensions,
#[structopt(short, long, default_value = "HASH", parse(try_from_str = parse_checking_method), help = "Search method (SIZE, HASH, HASHMB)", long_help = "Methods to search files.\nSIZE - The fastest method, checking by the file's size,\nHASHMB - More accurate but slower, checking by the hash of the file's first mibibyte or\nHASH - The slowest method, checking by the hash of the entire file")]
search_method: CheckingMethod,
#[structopt(short = "D", long, default_value = "AEO", parse(try_from_str = parse_delete_method), help = "Delete method (AEN, AEO, ON, OO)", long_help = "Methods to delete the files.\nAEN - All files except the newest,\nAEO - All files except the oldest,\nON - Only 1 file, the newest,\nOO - Only 1 file, the oldest")]
delete_method: DeleteMethod,
#[structopt(flatten)]
not_recursive: NotRecursive,
},
#[structopt(name = "empty-folders", about = "Finds emtpty folders")]
EmptyFolders {
#[structopt(flatten)]
directories: Directories,
#[structopt(short = "D", long, help = "Delete found folders")]
delete_folders: bool,
},
#[structopt(name = "big", about = "Finds big files")]
BiggestFiles {
#[structopt(flatten)]
directories: Directories,
#[structopt(flatten)]
excluded_directories: ExludedDirectories,
#[structopt(flatten)]
excluded_items: ExludedItems,
#[structopt(flatten)]
allowed_extensions: AllowedExtensions,
#[structopt(short, long, default_value = "50", help = "Number of files to be shown")]
number_of_files: usize,
#[structopt(flatten)]
not_recursive: NotRecursive,
},
#[structopt(name = "empty-files", about = "Finds emtpy files")]
EmptyFiles {
#[structopt(flatten)]
directories: Directories,
#[structopt(flatten)]
excluded_directories: ExludedDirectories,
#[structopt(flatten)]
excluded_items: ExludedItems,
#[structopt(flatten)]
allowed_extensions: AllowedExtensions,
#[structopt(short = "D", long, help = "Delete found files")]
delete_files: bool,
#[structopt(flatten)]
not_recursive: NotRecursive,
},
#[structopt(name = "temp", about = "Finds temporary files")]
Temporary {
#[structopt(flatten)]
directories: Directories,
#[structopt(flatten)]
excluded_directories: ExludedDirectories,
#[structopt(flatten)]
excluded_items: ExludedItems,
#[structopt(short = "D", long, help = "Delete found files")]
delete_files: bool,
#[structopt(flatten)]
not_recursive: NotRecursive,
},
}
#[derive(Debug, StructOpt)]
pub struct Directories {
#[structopt(short, long, parse(from_os_str), help = "Directorie(s) to search", long_help = "List of directorie(s) which will be searched(absolute path)")]
pub directories: Vec<PathBuf>,
}
impl Directories {
pub fn not_empty(&self) {
if self.directories.is_empty() {
eprintln!("error: At least one directory should be provided.");
process::exit(1);
}
}
}
#[derive(Debug, StructOpt)]
pub struct ExludedDirectories {
#[structopt(short, long, parse(from_os_str), help = "Exluded directorie(s)", long_help = "List of directorie(s) which will be excluded from search(absolute path)")]
pub excluded_directories: Vec<PathBuf>,
}
#[derive(Debug, StructOpt)]
pub struct ExludedItems {
#[structopt(short = "E", long, parse(from_os_str), help = "Exluded item(s)", long_help = "List of excluded item(s) which contains * wildcard(may be slow, so use -e where possible)")]
pub excluded_items: Vec<PathBuf>,
}
#[derive(Debug, StructOpt)]
pub struct AllowedExtensions {
#[structopt(
short = "x",
long,
help = "Allowed file extension(s)",
long_help = "List of checked files with provided extension(s). There are also helpful macros which allow to easy use a typcal extensions like:\nIMAGE(\"jpg,kra,gif,png,bmp,tiff,webp,hdr,svg\"),\nTEXT(\"txt,doc,docx,odt,rtf\"),\nVIDEO(\"mp4,flv,mkv,webm,vob,ogv,gifv,avi,mov,wmv,mpg,m4v,m4p,mpeg,3gp\") or\nMUSIC(\"mp3,flac,ogg,tta,wma,webm\")"
)]
pub allowed_extensions: Vec<String>,
}
#[derive(Debug, StructOpt)]
pub struct NotRecursive {
#[structopt(short = "R", long, help = "Prevents from recursive check of folders")]
pub not_recursive: bool,
}
fn parse_checking_method(src: &str) -> Result<CheckingMethod, &'static str> {
match src.to_ascii_lowercase().as_str() {
"size" => Ok(CheckingMethod::Size),
"hash" => Ok(CheckingMethod::Hash),
"hashmb" => Ok(CheckingMethod::HashMB),
_ => Err("Couldn't parse the search method (allowed: SIZE, HASH, HASHMB)"),
}
}
fn parse_delete_method(src: &str) -> Result<DeleteMethod, &'static str> {
match src.to_ascii_lowercase().as_str() {
"aen" => Ok(DeleteMethod::AllExceptNewest),
"aeo" => Ok(DeleteMethod::AllExceptOldest),
"on" => Ok(DeleteMethod::OneNewest),
"oo" => Ok(DeleteMethod::OneOldest),
_ => Err("Couldn't parse the delete method (allowed: AEN, AEO, ON, OO)"),
}
}

View file

@ -1,395 +1,155 @@
mod arguments_pair;
mod commands;
use crate::arguments_pair::ArgumentsPair;
use czkawka_core::common_traits::*;
use czkawka_core::*;
use std::{env, process};
use commands::Commands;
use czkawka_core::{
big_file::BigFile,
common_traits::*,
duplicate::DuplicateFinder,
empty_files::{self, EmptyFiles},
empty_folder::EmptyFolder,
temporary::{self, Temporary},
};
use std::{path::PathBuf, process};
use structopt::StructOpt;
fn path_list_to_str(path_list: Vec<PathBuf>) -> String {
let path_list: Vec<String> = path_list.into_iter().filter_map(|a| a.into_os_string().into_string().ok()).collect();
path_list.join(",")
}
fn main() {
// Parse argument
let all_arguments: Vec<String> = env::args().skip(1).collect(); // Not need to check program name
let mut commands_arguments: Vec<String> = Vec::new();
let command = Commands::from_args();
#[cfg(debug_assertions)]
println!("{:?}", all_arguments);
println!("{:?}", command);
// No arguments, so we print help to allow user to learn more about program
if all_arguments.is_empty() {
print_help();
process::exit(0);
}
match command {
Commands::Duplicates {
directories,
excluded_directories,
excluded_items,
min_size,
allowed_extensions,
search_method,
delete_method,
not_recursive,
} => {
directories.not_empty();
// Assigning commands with arguments
let mut arguments: Vec<ArgumentsPair> = Vec::new();
for argument in all_arguments {
if argument.starts_with("--") {
commands_arguments.push(argument);
} else if argument.starts_with('-') {
let a: ArgumentsPair = ArgumentsPair { command: argument, argument: Option::None };
arguments.push(a);
} else {
if arguments.is_empty() {
println!("FATAL ERROR: Trying to use {} without any arguments(like -i -e -delete)", argument);
if min_size == 0 {
eprintln!("error: Minimum file size must be at least 1 byte.");
process::exit(1);
}
if arguments[arguments.len() - 1].argument != Option::None {
println!(
"FATAL ERROR: Trying set second parameter \"{}\" for \"{}\" which already have this parameter \"{}\" ",
argument,
arguments[arguments.len() - 1].command,
arguments[arguments.len() - 1].argument.as_ref().unwrap()
); // This may be changed in future to support 2 or more attributes with space
process::exit(1);
}
let last_element = arguments.len() - 1;
arguments[last_element].argument = Option::from(argument);
}
}
#[cfg(debug_assertions)]
for a in &arguments {
println!(
"Argument number {} - {}",
a.command,
match &a.argument {
Some(t) => t.clone(),
None => "NO_ARGUMENT".to_string(),
}
);
}
let mut df = DuplicateFinder::new();
if commands_arguments.is_empty() {
println! {"FATAL ERROR: Missing type of app which you want to run, please read help for more info."};
process::exit(0);
}
match commands_arguments[0].as_ref() {
"--d" => {
let mut df = duplicate::DuplicateFinder::new();
if ArgumentsPair::has_command(&arguments, "-i") {
df.set_included_directory(ArgumentsPair::get_argument(&arguments, "-i", false));
} else {
println!("FATAL ERROR: Parameter -i with set of included files is required.");
process::exit(1);
}
if ArgumentsPair::has_command(&arguments, "-e") {
df.set_excluded_directory(ArgumentsPair::get_argument(&arguments, "-e", false));
}
if ArgumentsPair::has_command(&arguments, "-s") {
let min_size = match ArgumentsPair::get_argument(&arguments, "-s", false).parse::<u64>() {
Ok(t) => {
if t == 0 {
println!("ERROR: Minimum file size must be at least 1 byte.");
1
} else {
t
}
}
Err(_) => {
println!("FATAL ERROR: \"{}\" is not valid file size(allowed range <1,u64::max>)", ArgumentsPair::get_argument(&arguments, "-s", false));
process::exit(1);
}
};
df.set_min_file_size(min_size);
}
if ArgumentsPair::has_command(&arguments, "-x") {
df.set_allowed_extensions(ArgumentsPair::get_argument(&arguments, "-x", false));
}
if ArgumentsPair::has_command(&arguments, "-k") {
df.set_excluded_items(ArgumentsPair::get_argument(&arguments, "-k", false));
}
if ArgumentsPair::has_command(&arguments, "-o") {
df.set_recursive_search(false);
}
df.set_check_method(duplicate::CheckingMethod::Hash); // Default
if ArgumentsPair::has_command(&arguments, "-l") {
let argument_name = ArgumentsPair::get_argument(&arguments, "-l", false).to_lowercase();
if argument_name == "size" {
df.set_check_method(duplicate::CheckingMethod::Size);
} else if argument_name == "hash" {
df.set_check_method(duplicate::CheckingMethod::Hash);
} else if argument_name == "hashmb" {
df.set_check_method(duplicate::CheckingMethod::HashMB);
} else {
println!("-l can only have values hash or size");
process::exit(1);
}
}
df.set_delete_method(duplicate::DeleteMethod::None);
if ArgumentsPair::has_command(&arguments, "-delete") {
let argument_name = ArgumentsPair::get_argument(&arguments, "-delete", true).to_lowercase();
if argument_name == "aen" {
df.set_delete_method(duplicate::DeleteMethod::AllExceptNewest);
} else if argument_name == "aeo" {
df.set_delete_method(duplicate::DeleteMethod::AllExceptOldest);
} else if argument_name == "on" {
df.set_delete_method(duplicate::DeleteMethod::OneNewest);
} else if argument_name == "oo" {
df.set_delete_method(duplicate::DeleteMethod::OneOldest);
} else if argument_name == "" {
// Default
df.set_delete_method(duplicate::DeleteMethod::AllExceptOldest);
} else {
println!(
"Invalid argument {} for command -delete, available arguments - aen(All except newest one), aeo(All except oldest one), on(Only one newest), oo(Only one oldest)",
argument_name
);
process::exit(1);
}
}
df.set_included_directory(path_list_to_str(directories.directories));
df.set_excluded_directory(path_list_to_str(excluded_directories.excluded_directories));
df.set_excluded_items(path_list_to_str(excluded_items.excluded_items));
df.set_min_file_size(min_size);
df.set_allowed_extensions(allowed_extensions.allowed_extensions.join(","));
df.set_check_method(search_method);
df.set_delete_method(delete_method);
df.set_recursive_search(!not_recursive.not_recursive);
df.find_duplicates();
#[allow(clippy::collapsible_if)]
if ArgumentsPair::has_command(&arguments, "-f") {
let file_name = match ArgumentsPair::get_argument(&arguments, "-f", true).as_str() {
"" => "results.txt".to_string(),
t => t.to_string(),
};
if !df.save_results_to_file(file_name.as_str()) {
df.get_text_messages().print_messages();
process::exit(1);
}
}
#[cfg(not(debug_assertions))] // This will show too much probably unnecessary data to debug, comment line only if needed
df.print_results();
df.get_text_messages().print_messages();
}
"--h" | "--help" => {
print_help();
}
"--e" => {
let mut ef = empty_folder::EmptyFolder::new();
Commands::EmptyFolders { directories, delete_folders } => {
directories.not_empty();
if ArgumentsPair::has_command(&arguments, "-i") {
ef.set_included_directory(ArgumentsPair::get_argument(&arguments, "-i", false));
} else {
println!("FATAL ERROR: Parameter -i with set of included files is required.");
process::exit(1);
}
let mut ef = EmptyFolder::new();
if ArgumentsPair::has_command(&arguments, "-delete") {
ef.set_delete_folder(true);
}
ef.set_included_directory(path_list_to_str(directories.directories));
ef.set_delete_folder(delete_folders);
ef.find_empty_folders();
#[allow(clippy::collapsible_if)]
if ArgumentsPair::has_command(&arguments, "-f") {
let file_name = match ArgumentsPair::get_argument(&arguments, "-f", true).as_str() {
"" => "results.txt".to_string(),
t => t.to_string(),
};
if !ef.save_results_to_file(file_name.as_str()) {
ef.get_text_messages().print_messages();
process::exit(1);
}
}
#[cfg(not(debug_assertions))] // This will show too much probably unnecessary data to debug, comment line only if needed
ef.print_results();
ef.get_text_messages().print_messages();
}
"--b" => {
let mut bf = big_file::BigFile::new();
Commands::BiggestFiles {
directories,
excluded_directories,
excluded_items,
allowed_extensions,
number_of_files,
not_recursive,
} => {
directories.not_empty();
if ArgumentsPair::has_command(&arguments, "-i") {
bf.set_included_directory(ArgumentsPair::get_argument(&arguments, "-i", false));
} else {
println!("FATAL ERROR: Parameter -i with set of included files is required.");
process::exit(1);
}
if ArgumentsPair::has_command(&arguments, "-e") {
bf.set_excluded_directory(ArgumentsPair::get_argument(&arguments, "-e", false));
}
let mut bf = BigFile::new();
if ArgumentsPair::has_command(&arguments, "-l") {
let number_of_files = match ArgumentsPair::get_argument(&arguments, "-l", false).parse::<usize>() {
Ok(t) => {
if t == 0 {
println!("ERROR: Minimum one biggest file must be showed..");
1
} else {
t
}
}
Err(_) => {
println!("FATAL ERROR: \"{}\" is not valid number of files to show(allowed range <1,usize::max>)", ArgumentsPair::get_argument(&arguments, "-s", false));
process::exit(1);
}
};
bf.set_number_of_files_to_check(number_of_files);
}
if ArgumentsPair::has_command(&arguments, "-x") {
bf.set_allowed_extensions(ArgumentsPair::get_argument(&arguments, "-x", false));
}
if ArgumentsPair::has_command(&arguments, "-k") {
bf.set_excluded_items(ArgumentsPair::get_argument(&arguments, "-k", false));
}
if ArgumentsPair::has_command(&arguments, "-o") {
bf.set_recursive_search(false);
}
bf.set_included_directory(path_list_to_str(directories.directories));
bf.set_excluded_directory(path_list_to_str(excluded_directories.excluded_directories));
bf.set_excluded_items(path_list_to_str(excluded_items.excluded_items));
bf.set_allowed_extensions(allowed_extensions.allowed_extensions.join(","));
bf.set_number_of_files_to_check(number_of_files);
bf.set_recursive_search(!not_recursive.not_recursive);
bf.find_big_files();
#[allow(clippy::collapsible_if)]
if ArgumentsPair::has_command(&arguments, "-f") {
let file_name = match ArgumentsPair::get_argument(&arguments, "-f", true).as_str() {
"" => "results.txt".to_string(),
t => t.to_string(),
};
if !bf.save_results_to_file(file_name.as_str()) {
bf.get_text_messages().print_messages();
process::exit(1);
}
}
#[cfg(not(debug_assertions))] // This will show too much probably unnecessary data to debug, comment line only if needed
bf.print_results();
bf.get_text_messages().print_messages();
}
"--y" => {
let mut yf = empty_files::EmptyFiles::new();
Commands::EmptyFiles {
directories,
excluded_directories,
excluded_items,
allowed_extensions,
delete_files,
not_recursive,
} => {
directories.not_empty();
if ArgumentsPair::has_command(&arguments, "-i") {
yf.set_included_directory(ArgumentsPair::get_argument(&arguments, "-i", false));
} else {
println!("FATAL ERROR: Parameter -i with set of included files is required.");
process::exit(1);
let mut ef = EmptyFiles::new();
ef.set_included_directory(path_list_to_str(directories.directories));
ef.set_excluded_directory(path_list_to_str(excluded_directories.excluded_directories));
ef.set_excluded_items(path_list_to_str(excluded_items.excluded_items));
ef.set_allowed_extensions(allowed_extensions.allowed_extensions.join(","));
ef.set_recursive_search(!not_recursive.not_recursive);
if delete_files {
ef.set_delete_method(empty_files::DeleteMethod::Delete);
}
if ArgumentsPair::has_command(&arguments, "-e") {
yf.set_excluded_directory(ArgumentsPair::get_argument(&arguments, "-e", false));
}
if ArgumentsPair::has_command(&arguments, "-k") {
yf.set_excluded_items(ArgumentsPair::get_argument(&arguments, "-k", false));
}
if ArgumentsPair::has_command(&arguments, "-o") {
yf.set_recursive_search(false);
}
if ArgumentsPair::has_command(&arguments, "-delete") {
yf.set_delete_method(empty_files::DeleteMethod::Delete);
}
yf.find_empty_files();
#[allow(clippy::collapsible_if)]
if ArgumentsPair::has_command(&arguments, "-f") {
let file_name = match ArgumentsPair::get_argument(&arguments, "-f", true).as_str() {
"" => "results.txt".to_string(),
t => t.to_string(),
};
if !yf.save_results_to_file(file_name.as_str()) {
yf.get_text_messages().print_messages();
process::exit(1);
}
}
ef.find_empty_files();
#[cfg(not(debug_assertions))] // This will show too much probably unnecessary data to debug, comment line only if needed
yf.print_results();
yf.get_text_messages().print_messages();
ef.print_results();
ef.get_text_messages().print_messages();
}
"--t" => {
let mut tf = temporary::Temporary::new();
Commands::Temporary {
directories,
excluded_directories,
excluded_items,
delete_files,
not_recursive,
} => {
directories.not_empty();
if ArgumentsPair::has_command(&arguments, "-i") {
tf.set_included_directory(ArgumentsPair::get_argument(&arguments, "-i", false));
} else {
println!("FATAL ERROR: Parameter -i with set of included files is required.");
process::exit(1);
}
let mut tf = Temporary::new();
if ArgumentsPair::has_command(&arguments, "-e") {
tf.set_excluded_directory(ArgumentsPair::get_argument(&arguments, "-e", false));
}
tf.set_included_directory(path_list_to_str(directories.directories));
tf.set_excluded_directory(path_list_to_str(excluded_directories.excluded_directories));
tf.set_excluded_items(path_list_to_str(excluded_items.excluded_items));
tf.set_recursive_search(!not_recursive.not_recursive);
if ArgumentsPair::has_command(&arguments, "-k") {
tf.set_excluded_items(ArgumentsPair::get_argument(&arguments, "-k", false));
}
if ArgumentsPair::has_command(&arguments, "-o") {
tf.set_recursive_search(false);
}
if ArgumentsPair::has_command(&arguments, "-delete") {
if delete_files {
tf.set_delete_method(temporary::DeleteMethod::Delete);
}
tf.find_temporary_files();
#[allow(clippy::collapsible_if)]
if ArgumentsPair::has_command(&arguments, "-f") {
let file_name = match ArgumentsPair::get_argument(&arguments, "-f", true).as_str() {
"" => "results.txt".to_string(),
t => t.to_string(),
};
if !tf.save_results_to_file(file_name.as_str()) {
tf.get_text_messages().print_messages();
process::exit(1);
}
}
#[cfg(not(debug_assertions))] // This will show too much probably unnecessary data to debug, comment line only if needed
tf.print_results();
tf.get_text_messages().print_messages();
}
"--version" | "--v" => {
println!("Czkawka CLI {}", CZKAWKA_VERSION);
process::exit(0);
}
argum => {
println!("FATAL ERROR: \"{}\" argument is not supported, check help for more info.", argum);
process::exit(1);
}
};
}
fn print_help() {
println!(
r###"
[Main commands]:
--help / --h - prints help, also works without any arguments
--d <-i directories_to_search> [-e excluded_directories = ""] [-k excluded_items = ""] [-s min_size = 1024] [-x allowed_extension = ""] [-l type_of_search = "hash"] [-o] [-f file_to_save = "results.txt"] [-delete = "aeo"] - finds duplicates files
--e <-i directories_to_search> [-e excluded_directories = ""] [-o] [-f file_to_save = "results.txt"] [-delete] - finds empty folders
--b <-i directories_to_search> [-e excluded_directories = ""] [-k excluded_items = ""] [-o] [-p number_of_files = 50] [-x allowed_extension = ""] [-f file_to_save = "results.txt"] - finds biggest files
--y <-i directories_to_search> [-e excluded_directories = ""] [-k excluded_items = ""] [-o] [-f file_to_save = "results.txt"] [-delete] - finds empty files
--t <-i directories_to_search> [-e excluded_directories = ""] [-k excluded_items = ""] [-o] [-f file_to_save = "results.txt"] [-delete] - finds temporary files
--version / --v - prints program name and version
[Options]:
-i directories_to_search - list of directories which should will be searched(absolute path)
-e excluded_directories - list of directories which will be excluded from search(absolute path)
-k excluded_items - list of excluded items which contains * wildcard(may be slow, so use exclude_directories where possible)
-o - this options prevents from recursive check of folders
-s min_size - minimum size of checked files in bytes, assigning bigger value may speed up searching.
-p number_of_files - number of showed the biggest files.
-x allowed_extension - list of checked files with provided extensions. There are also helpful macros which allow to easy use a typcal extensions like IMAGE("jpg,kra,gif,png,bmp,tiff,webp,hdr,svg"), TEXT, VIDEO or MUSIC.
-l type_of_search - allows to use fastest method which takes into account only size(SIZE), more accurate which takes into account hash of only first 1MB of file(HASHMB) or fully accurate(but the slowest solution) which check hash of all file(HASH).
-f file_to_save - saves results to file
-delete - delete found files, in duplicate finder by default remove all files in group except the most oldest one but it can take arguments: aen(All except newest one), aeo(All except oldest one), on(Only one newest), oo(Only one oldest)
[Usage example]:
czkawka --d -i "/home/rafal/,/home/szczekacz" -e "/home/rafal/Pulpit,/home/rafal/Obrazy" -s 25 -x "7z,rar,IMAGE" -l "hashmb" -f "results.txt" -delete "aeo"
czkawka --e -i "/home/rafal/rr, /home/gateway" -f "results.txt"
czkawka --b -i "/home/rafal/,/home/piszczal" -e "/home/rafal/Roman" -p 25 -x "VIDEO" " -f "results.txt"
czkawka --y -i "/home/rafal/" -e "/etc/" -o -f "results.txt"
czkawka --t -i "/home/rafal/" -k "*/.git/*,*/tmp*,*Pulpit" " -f "results.txt" -delete
"###
);
}
}