1
0
Fork 0
mirror of synced 2024-04-28 01:22:53 +12:00

Changed to StructOpt for CLI argument parser. (#37)

Changed to StructOpt for CLI argument parser.
This commit is contained in:
Meir Klemfner 2020-10-05 00:36:49 +03:00 committed by GitHub
parent e60c042bb9
commit e4af905078
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 391 additions and 356 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");
}
}

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

@ -0,0 +1,192 @@
use czkawka_core::duplicate::{CheckingMethod, DeleteMethod};
use std::path::PathBuf;
use structopt::StructOpt;
#[derive(Debug, StructOpt)]
#[structopt(name = "czkawka", help_message = HELP_MESSAGE, template = HELP_TEMPLATE)]
pub enum Commands {
#[structopt(name = "dup", about = "Finds duplicate files", help_message = HELP_MESSAGE, after_help = "EXAMPLE:\n czkawka dup -d /home/rafal -e /home/rafal/Obrazy -m 25 -x 7z rar IMAGE -s hashmb -f results.txt -D aeo")]
Duplicates {
#[structopt(flatten)]
directories: Directories,
#[structopt(flatten)]
excluded_directories: ExludedDirectories,
#[structopt(flatten)]
excluded_items: ExludedItems,
#[structopt(short, long, parse(try_from_str = parse_min_size), 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)]
file_to_save: FileToSave,
#[structopt(flatten)]
not_recursive: NotRecursive,
},
#[structopt(name = "empty-folders", about = "Finds emtpty folders", help_message = HELP_MESSAGE, after_help = "EXAMPLE:\n czkawka empty-folders -d /home/rafal/rr /home/gateway -f results.txt")]
EmptyFolders {
#[structopt(flatten)]
directories: Directories,
#[structopt(short = "D", long, help = "Delete found folders")]
delete_folders: bool,
#[structopt(flatten)]
file_to_save: FileToSave,
},
#[structopt(name = "big", about = "Finds big files", help_message = HELP_MESSAGE, after_help = "EXAMPLE:\n czkawka big -d /home/rafal/ /home/piszczal -e /home/rafal/Roman -n 25 -x VIDEO -f results.txt")]
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)]
file_to_save: FileToSave,
#[structopt(flatten)]
not_recursive: NotRecursive,
},
#[structopt(name = "empty-files", about = "Finds emtpy files", help_message = HELP_MESSAGE, after_help = "EXAMPLE:\n czkawka empty-files -d /home/rafal /home/szczekacz -e /home/rafal/Pulpit -R -f results.txt")]
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)]
file_to_save: FileToSave,
#[structopt(flatten)]
not_recursive: NotRecursive,
},
#[structopt(name = "temp", about = "Finds temporary files", help_message = HELP_MESSAGE, after_help = "EXAMPLE:\n czkawka temp -d /home/rafal/ -E */.git */tmp* *Pulpit -f results.txt -D")]
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)]
file_to_save: FileToSave,
#[structopt(flatten)]
not_recursive: NotRecursive,
},
}
#[derive(Debug, StructOpt)]
pub struct Directories {
#[structopt(short, long, parse(from_os_str), required = true, help = "Directorie(s) to search", long_help = "List of directorie(s) which will be searched(absolute path)")]
pub directories: Vec<PathBuf>,
}
#[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\")\n "
)]
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,
}
#[derive(Debug, StructOpt)]
pub struct FileToSave {
#[structopt(short, long, value_name = "file-name", help = "Saves the results into the file")]
pub file_to_save: Option<PathBuf>,
}
impl FileToSave {
pub fn file_name(&self) -> Option<&str> {
if let Some(file_name) = &self.file_to_save {
return file_name.to_str();
}
None
}
}
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)"),
}
}
fn parse_min_size(src: &str) -> Result<u64, String> {
match src.parse::<u64>() {
Ok(min_size) => {
if min_size > 0 {
Ok(min_size)
} else {
Err("Minimum file size must be at least 1 byte".to_string())
}
}
Err(e) => Err(e.to_string()),
}
}
static HELP_MESSAGE: &str = "Prints help information (--help will give more information)";
const HELP_TEMPLATE: &str = r#"
{bin} {version}
USAGE:
{usage} [SCFLAGS] [SCOPTIONS]
FLAGS:
{flags}
SUBCOMMANDS:
{subcommands}
try "{usage} -h" to get more info about a specific tool
EXAMPLES:
{bin} dup -d /home/rafal -e /home/rafal/Obrazy -m 25 -x 7z rar IMAGE -s hashmb -f results.txt -D aeo
{bin} empty-folders -d /home/rafal/rr /home/gateway -f results.txt
{bin} big -d /home/rafal/ /home/piszczal -e /home/rafal/Roman -n 25 -x VIDEO -f results.txt
{bin} empty-files -d /home/rafal /home/szczekacz -e /home/rafal/Pulpit -R -f results.txt
{bin} temp -d /home/rafal/ -E */.git */tmp* *Pulpit -f results.txt -D"#;

View file

@ -1,157 +1,58 @@
mod arguments_pair;
mod commands;
use crate::arguments_pair::ArgumentsPair;
use commands::Commands;
#[allow(unused_imports)] // It is used in release for print_results().
use czkawka_core::common_traits::*;
use czkawka_core::*;
use std::{env, process};
use czkawka_core::{
big_file::BigFile,
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,
file_to_save,
not_recursive,
} => {
let mut df = DuplicateFinder::new();
// 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);
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(),
}
);
}
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 minimal_file_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_minimal_file_size(minimal_file_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_minimal_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()) {
if let Some(file_name) = file_to_save.file_name() {
if !df.save_results_to_file(file_name) {
df.get_text_messages().print_messages();
process::exit(1);
}
@ -159,35 +60,18 @@ fn main() {
#[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, file_to_save } => {
let mut ef = EmptyFolder::new();
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);
}
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()) {
if let Some(file_name) = file_to_save.file_name() {
if !ef.save_results_to_file(file_name) {
ef.get_text_messages().print_messages();
process::exit(1);
}
@ -195,58 +79,30 @@ fn main() {
#[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,
file_to_save,
not_recursive,
} => {
let mut bf = BigFile::new();
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));
}
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()) {
if let Some(file_name) = file_to_save.file_name() {
if !bf.save_results_to_file(file_name) {
bf.get_text_messages().print_messages();
process::exit(1);
}
@ -254,89 +110,65 @@ fn main() {
#[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,
file_to_save,
not_recursive,
} => {
let mut ef = EmptyFiles::new();
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);
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));
}
ef.find_empty_files();
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();
if let Some(file_name) = file_to_save.file_name() {
if !ef.save_results_to_file(file_name) {
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
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,
file_to_save,
not_recursive,
} => {
let mut tf = Temporary::new();
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);
}
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, "-e") {
tf.set_excluded_directory(ArgumentsPair::get_argument(&arguments, "-e", false));
}
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()) {
if let Some(file_name) = file_to_save.file_name() {
if !tf.save_results_to_file(file_name) {
tf.get_text_messages().print_messages();
process::exit(1);
}
@ -344,52 +176,7 @@ fn main() {
#[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 minimal_file_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 minimal_file_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
"###
);
}
}