1
0
Fork 0
mirror of synced 2024-05-04 12:33:11 +12:00

Removed not using packages, added better menu

This commit is contained in:
Rafał Mikrut 2020-08-30 14:00:22 +02:00
parent 0c1550d55e
commit f9fa7d3879
5 changed files with 203 additions and 291 deletions

195
Cargo.lock generated
View file

@ -1,14 +1,5 @@
# This file is automatically @generated by Cargo. # This file is automatically @generated by Cargo.
# It is not intended for manual editing. # It is not intended for manual editing.
[[package]]
name = "aho-corasick"
version = "0.7.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "043164d8ba5c4c3035fec9bbee8647c0261d788f3474306f93bb65901cae0e86"
dependencies = [
"memchr",
]
[[package]] [[package]]
name = "arrayref" name = "arrayref"
version = "0.3.6" version = "0.3.6"
@ -21,12 +12,6 @@ version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8" checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8"
[[package]]
name = "autocfg"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
[[package]] [[package]]
name = "blake3" name = "blake3"
version = "0.3.6" version = "0.3.6"
@ -60,53 +45,6 @@ version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc"
[[package]]
name = "crossbeam-channel"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ee0cc8804d5393478d743b035099520087a5186f3b93fa58cec08fa62407b6"
dependencies = [
"cfg-if",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-deque"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f02af974daeee82218205558e51ec8768b48cf524bd01d550abe5573a608285"
dependencies = [
"crossbeam-epoch",
"crossbeam-utils",
"maybe-uninit",
]
[[package]]
name = "crossbeam-epoch"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace"
dependencies = [
"autocfg",
"cfg-if",
"crossbeam-utils",
"lazy_static",
"maybe-uninit",
"memoffset",
"scopeguard",
]
[[package]]
name = "crossbeam-utils"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8"
dependencies = [
"autocfg",
"cfg-if",
"lazy_static",
]
[[package]] [[package]]
name = "crypto-mac" name = "crypto-mac"
version = "0.8.0" version = "0.8.0"
@ -122,9 +60,7 @@ name = "czkawka"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"blake3", "blake3",
"multimap", "humansize",
"rayon",
"regex",
] ]
[[package]] [[package]]
@ -136,12 +72,6 @@ dependencies = [
"generic-array", "generic-array",
] ]
[[package]]
name = "either"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd56b59865bce947ac5958779cfa508f6c3b9497cc762b7e24a12d11ccde2c4f"
[[package]] [[package]]
name = "generic-array" name = "generic-array"
version = "0.14.4" version = "0.14.4"
@ -153,120 +83,10 @@ dependencies = [
] ]
[[package]] [[package]]
name = "hermit-abi" name = "humansize"
version = "0.1.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3deed196b6e7f9e44a2ae8d94225d80302d81208b1bb673fd21fe634645c85a9"
dependencies = [
"libc",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.76"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "755456fae044e6fa1ebbbd1b3e902ae19e73097ed4ed87bb79934a867c007bc3"
[[package]]
name = "maybe-uninit"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00"
[[package]]
name = "memchr"
version = "2.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400"
[[package]]
name = "memoffset"
version = "0.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c198b026e1bbf08a937e94c6c60f9ec4a2267f5b0d2eec9c1b21b061ce2be55f"
dependencies = [
"autocfg",
]
[[package]]
name = "multimap"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1255076139a83bb467426e7f8d0134968a8118844faa755985e077cf31850333"
dependencies = [
"serde",
]
[[package]]
name = "num_cpus"
version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3"
dependencies = [
"hermit-abi",
"libc",
]
[[package]]
name = "rayon"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cfd016f0c045ad38b5251be2c9c0ab806917f82da4d36b2a327e5166adad9270"
dependencies = [
"autocfg",
"crossbeam-deque",
"either",
"rayon-core",
]
[[package]]
name = "rayon-core"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91739a34c4355b5434ce54c9086c5895604a9c278586d1f1aa95e04f66b525a0"
dependencies = [
"crossbeam-channel",
"crossbeam-deque",
"crossbeam-utils",
"lazy_static",
"num_cpus",
]
[[package]]
name = "regex"
version = "1.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c3780fcf44b193bc4d09f36d2a3c87b251da4a046c87795a0d35f4f927ad8e6"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
"thread_local",
]
[[package]]
name = "regex-syntax"
version = "0.6.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26412eb97c6b088a6997e05f69403a802a92d520de2f8e63c2b65f9e0f47c4e8"
[[package]]
name = "scopeguard"
version = "1.1.0" version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" checksum = "b6cab2627acfc432780848602f3f558f7e9dd427352224b0d9324025796d2a5e"
[[package]]
name = "serde"
version = "1.0.115"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e54c9a88f2da7238af84b5101443f0c0d0a3bbdc455e34a5c9497b1903ed55d5"
[[package]] [[package]]
name = "subtle" name = "subtle"
@ -274,15 +94,6 @@ version = "2.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "502d53007c02d7605a05df1c1a73ee436952781653da5d0bf57ad608f66932c1" checksum = "502d53007c02d7605a05df1c1a73ee436952781653da5d0bf57ad608f66932c1"
[[package]]
name = "thread_local"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14"
dependencies = [
"lazy_static",
]
[[package]] [[package]]
name = "typenum" name = "typenum"
version = "1.12.0" version = "1.12.0"

View file

@ -5,7 +5,7 @@ authors = ["Rafał Mikrut <mikrutrafal@protonmail.com>"]
edition = "2018" edition = "2018"
[dependencies] [dependencies]
rayon = "1.4.0" humansize = "1.1.0"
#rayon = "1.4.0"
blake3 = "0.3.6" blake3 = "0.3.6"
multimap = "0.8.2" #regex = "1.3.9"
regex = "1.3.9"

View file

@ -4,12 +4,18 @@ It is in very early development, so most of the functions aren't added and doesn
## Done ## Done
- Basic menu - Basic menu(need refactoring)
- Duplicated file finding
- Including and excluding directories(absolute pathes)
- Option to remove file
- Fast(by size) or accurate(by hash) file checking
-
## TODO ## TODO
- Graphical UI(GTK)
- Duplicated file finding - Duplicated file finding
- Setting include and exclude directories - saving to file
- support for * when excluding files and folders
- Graphical UI(GTK)
- Removing empty folders - Removing empty folders
- Files with debug symbols - Files with debug symbols
- Support for showing only duplicates with specific extension, name(Regex support needed) - Support for showing only duplicates with specific extension, name(Regex support needed)

View file

@ -1,4 +1,5 @@
// Todo, należy upewnić się, że ma wystarczające uprawnienia do odczytu i usuwania // Todo, należy upewnić się, że ma wystarczające uprawnienia do odczytu i usuwania
use humansize::{file_size_opts as options, FileSize};
use std::collections::{BTreeMap, HashMap}; use std::collections::{BTreeMap, HashMap};
use std::fs::{File, Metadata}; use std::fs::{File, Metadata};
use std::io::prelude::*; use std::io::prelude::*;
@ -6,15 +7,14 @@ use std::path::Path;
use std::time::SystemTime; use std::time::SystemTime;
use std::{fs, process}; use std::{fs, process};
const MIN_FILE_SIZE: u64 = 1000;
#[derive(PartialEq)] #[derive(PartialEq)]
#[allow(dead_code)] // For now I only use Hash method
pub enum CheckingMethod { pub enum CheckingMethod {
SIZE, SIZE,
HASH, HASH,
} }
pub struct DuplicateFinder { pub struct DuplicateFinder {
number_of_checked_files: usize, number_of_checked_files: usize,
number_of_ignored_files: usize, number_of_ignored_files: usize,
@ -24,6 +24,7 @@ pub struct DuplicateFinder {
files_with_identical_size: HashMap<u64, Vec<FileEntry>>, files_with_identical_size: HashMap<u64, Vec<FileEntry>>,
files_with_identical_hashes: BTreeMap<u64, Vec<Vec<FileEntry>>>, files_with_identical_hashes: BTreeMap<u64, Vec<Vec<FileEntry>>>,
allowed_extensions: Vec<String>, // jpg, jpeg, mp4 allowed_extensions: Vec<String>, // jpg, jpeg, mp4
lost_space: u64,
// excluded_items: Vec<String>, // excluded_items: Vec<String>,
excluded_directories: Vec<String>, excluded_directories: Vec<String>,
included_directories: Vec<String>, included_directories: Vec<String>,
@ -43,8 +44,9 @@ impl DuplicateFinder {
// excluded_items: vec![], // excluded_items: vec![],
excluded_directories: vec![], excluded_directories: vec![],
included_directories: vec![], included_directories: vec![],
min_file_size: 0, min_file_size: 1024,
allowed_extensions: vec![], allowed_extensions: vec![],
lost_space: 0,
} }
} }
@ -56,6 +58,7 @@ impl DuplicateFinder {
if check_method == CheckingMethod::HASH { if check_method == CheckingMethod::HASH {
self.check_files_hash(); self.check_files_hash();
} }
self.calculate_lost_space(&check_method);
self.print_duplicated_entries(&check_method); self.print_duplicated_entries(&check_method);
if delete_files { if delete_files {
self.delete_files(&check_method); self.delete_files(&check_method);
@ -77,6 +80,7 @@ impl DuplicateFinder {
allowed_extensions = allowed_extensions.replace("IMAGE", "jpg,kra,gif,png,bmp,tiff,webp,hdr,svg"); allowed_extensions = allowed_extensions.replace("IMAGE", "jpg,kra,gif,png,bmp,tiff,webp,hdr,svg");
allowed_extensions = allowed_extensions.replace("VIDEO", "mp4,flv,mkv,webm,vob,ogv,gifv,avi,mov,wmv,mpg,m4v,m4p,mpeg,3gp"); allowed_extensions = allowed_extensions.replace("VIDEO", "mp4,flv,mkv,webm,vob,ogv,gifv,avi,mov,wmv,mpg,m4v,m4p,mpeg,3gp");
allowed_extensions = allowed_extensions.replace("MUSIC", "mp3,flac,ogg,tta,wma,webm"); allowed_extensions = allowed_extensions.replace("MUSIC", "mp3,flac,ogg,tta,wma,webm");
allowed_extensions = allowed_extensions.replace("TEXT", "txt,doc,docx,odt,rtf");
let extensions: Vec<String> = allowed_extensions.split(',').map(String::from).collect(); let extensions: Vec<String> = allowed_extensions.split(',').map(String::from).collect();
for mut extension in extensions { for mut extension in extensions {
@ -178,8 +182,8 @@ impl DuplicateFinder {
process::exit(1); process::exit(1);
} }
if !Path::new(&directory).exists() { if !Path::new(&directory).exists() {
println!("Exclude Directory ERROR: Path {} doesn't exists.", directory); println!("Exclude Directory WARNING: Path {} doesn't exists.", directory);
process::exit(1); //process::exit(1); // Better just print warning witohut closing
} }
if !Path::new(&directory).exists() { if !Path::new(&directory).exists() {
println!("Exclude Directory ERROR: {} isn't folder.", directory); println!("Exclude Directory ERROR: {} isn't folder.", directory);
@ -198,6 +202,25 @@ impl DuplicateFinder {
//DuplicateFinder::print_time(start_time, SystemTime::now(), "set_exclude_directory".to_string()); //DuplicateFinder::print_time(start_time, SystemTime::now(), "set_exclude_directory".to_string());
} }
fn calculate_lost_space(&mut self, check_method: &CheckingMethod) {
let mut bytes: u64 = 0;
match check_method {
CheckingMethod::SIZE => {
for i in &self.files_with_identical_size {
bytes += i.0 * (i.1.len() as u64 - 1);
}
}
CheckingMethod::HASH => {
for i in &self.files_with_identical_hashes {
for j in i.1 {
bytes += i.0 * (j.len() as u64 - 1);
}
}
}
}
self.lost_space = bytes;
}
// TODO - Still isn't used but it will be probably required with GUI // TODO - Still isn't used but it will be probably required with GUI
// pub fn clear(&mut self) { // pub fn clear(&mut self) {
@ -257,7 +280,7 @@ impl DuplicateFinder {
if !self.allowed_extensions.is_empty() { if !self.allowed_extensions.is_empty() {
have_valid_extension = false; have_valid_extension = false;
for i in &self.allowed_extensions { for i in &self.allowed_extensions {
if file_name_lowercase.ends_with(&i.to_lowercase()) { if file_name_lowercase.ends_with(&(".".to_string() + &*i.to_lowercase().to_string())) {
have_valid_extension = true; have_valid_extension = true;
break; break;
} }
@ -266,7 +289,7 @@ impl DuplicateFinder {
have_valid_extension = true; have_valid_extension = true;
} }
if metadata.len() >= MIN_FILE_SIZE && have_valid_extension { if metadata.len() >= self.min_file_size && have_valid_extension {
let current_file_name = "".to_owned() + &current_folder + &entry_data.file_name().into_string().unwrap(); let current_file_name = "".to_owned() + &current_folder + &entry_data.file_name().into_string().unwrap();
// println!("File\t\t - {:?}", current_file_name); // DEBUG // println!("File\t\t - {:?}", current_file_name); // DEBUG
//file_to_check //file_to_check
@ -383,27 +406,27 @@ impl DuplicateFinder {
/// Setting include directories, panics when there is not directories available /// Setting include directories, panics when there is not directories available
fn debug_print(&self) { fn debug_print(&self) {
println!("---------------DEBUG PRINT---------------"); // println!("---------------DEBUG PRINT---------------");
println!("Number of all checked files - {}", self.number_of_checked_files); // println!("Number of all checked files - {}", self.number_of_checked_files);
println!("Number of all ignored files - {}", self.number_of_ignored_files); // println!("Number of all ignored files - {}", self.number_of_ignored_files);
println!("Number of all checked folders - {}", self.number_of_checked_folders); // println!("Number of all checked folders - {}", self.number_of_checked_folders);
println!("Number of all ignored things - {}", self.number_of_ignored_things); // println!("Number of all ignored things - {}", self.number_of_ignored_things);
println!("Number of duplicated files - {}", self.number_of_duplicated_files); // println!("Number of duplicated files - {}", self.number_of_duplicated_files);
let mut file_size: u64 = 0; // let mut file_size: u64 = 0;
for i in &self.files_with_identical_size { // for i in &self.files_with_identical_size {
file_size += i.1.len() as u64; // file_size += i.1.len() as u64;
} // }
println!("Files list size - {} ({})", self.files_with_identical_size.len(), file_size); // println!("Files list size - {} ({})", self.files_with_identical_size.len(), file_size);
let mut hashed_file_size: u64 = 0; // let mut hashed_file_size: u64 = 0;
for i in &self.files_with_identical_hashes { // for i in &self.files_with_identical_hashes {
for j in i.1 { // for j in i.1 {
hashed_file_size += j.len() as u64; // hashed_file_size += j.len() as u64;
} // }
} // }
println!("Hashed Files list size - {} ({})", self.files_with_identical_hashes.len(), hashed_file_size); // println!("Hashed Files list size - {} ({})", self.files_with_identical_hashes.len(), hashed_file_size);
println!("Excluded directories - {:?}", self.excluded_directories); // println!("Excluded directories - {:?}", self.excluded_directories);
println!("Included directories - {:?}", self.included_directories); // println!("Included directories - {:?}", self.included_directories);
println!("-----------------------------------------"); // println!("-----------------------------------------");
} }
#[allow(dead_code)] #[allow(dead_code)]
@ -420,7 +443,12 @@ impl DuplicateFinder {
number_of_groups += 1; number_of_groups += 1;
} }
} }
println!("Found {} files in {} groups with same content:", number_of_files, number_of_groups); println!(
"Found {} files in {} groups with same content which took {}:",
number_of_files,
number_of_groups,
self.lost_space.file_size(options::BINARY).unwrap()
);
for i in &self.files_with_identical_hashes { for i in &self.files_with_identical_hashes {
println!("Size - {}", i.0); println!("Size - {}", i.0);
for j in i.1 { for j in i.1 {
@ -437,21 +465,26 @@ impl DuplicateFinder {
number_of_files += i.1.len() as u64; number_of_files += i.1.len() as u64;
number_of_groups += 1; number_of_groups += 1;
} }
println!("Found {} files in {} groups with same size(may have different content):", number_of_files, number_of_groups); println!(
for i in &self.files_with_identical_size { "Found {} files in {} groups with same size(may have different content) which took {}:",
println!("Size - {}", i.0); number_of_files,
for j in i.1 { number_of_groups,
println!("{}", j.path); self.lost_space.file_size(options::BINARY).unwrap()
} );
println!(); // for i in &self.files_with_identical_size {
} // println!("Size - {}", i.0);
// for j in i.1 {
// println!("{}", j.path);
// }
// println!();
// }
} }
} }
DuplicateFinder::print_time(start_time, SystemTime::now(), "print_duplicated_entries".to_string()); DuplicateFinder::print_time(start_time, SystemTime::now(), "print_duplicated_entries".to_string());
} }
/// Remove unused entries when included or excluded overlaps with each other or are duplicated /// Remove unused entries when included or excluded overlaps with each other or are duplicated
/// ``` /// ```
/// let df : DuplicateFinder = saf // let df : DuplicateFinder = saf
/// ``` /// ```
fn optimize_directories(&mut self) { fn optimize_directories(&mut self) {
let start_time: SystemTime = SystemTime::now(); let start_time: SystemTime = SystemTime::now();
@ -616,13 +649,3 @@ struct FileEntry {
pub created_date: SystemTime, pub created_date: SystemTime,
pub modified_date: SystemTime, pub modified_date: SystemTime,
} }
impl FileEntry {
// pub fn return_copy(&self) -> FileEntry {
// let new_copy : FileEntry = FileEntry{
// path: self.path.to_string(),
// size: self.size,
// created_date: self.created_date,
// modified_date: self.modified_date
// };
// }
}

View file

@ -4,72 +4,110 @@ mod duplicate;
fn main() { fn main() {
// Parse argument // Parse argument
//
let mut all_arguments: Vec<String> = env::args().collect(); let mut all_arguments: Vec<String> = env::args().collect();
let number_of_arguments: usize = all_arguments.len() - 1;
let mut arguments: Vec<String> = Vec::new();
let mut commands_arguments: Vec<String> = Vec::new(); let mut commands_arguments: Vec<String> = Vec::new();
all_arguments.remove(0); // Removing program name from arguments all_arguments.remove(0); // Removing program name from arguments
for argument in all_arguments { // No arguments, so we print help to allow user to learn more about program
if argument.starts_with('-') { if all_arguments.is_empty() {
commands_arguments.push(argument);
} else {
arguments.push(argument);
}
}
println!("Number of arguments - {}", arguments.len());
for (index, argument) in arguments.iter().enumerate() {
println!("Argument number {} - {}", index, argument);
}
if number_of_arguments == 0 {
print_help(); print_help();
process::exit(0); process::exit(0);
} }
// Assigning commands with arguments
let mut arguments: Vec<ArgumentsPair> = Vec::new();
let mut can_pass_argument: bool = false;
for argument in 0..all_arguments.len() {
if all_arguments[argument].starts_with("--") {
commands_arguments.push(all_arguments[argument].clone());
} else if all_arguments[argument].starts_with('-') {
if argument + 1 < all_arguments.len() {
if all_arguments[argument + 1].starts_with("--") || all_arguments[argument + 1].starts_with('-') {
println!("FATAL ERROR: Missing argument for {}", all_arguments[argument]);
process::exit(1);
} else {
let a: ArgumentsPair = ArgumentsPair {
command: all_arguments[argument].clone(),
argument: all_arguments[argument + 1].clone(),
};
arguments.push(a);
can_pass_argument = true;
}
} else {
println!("FATAL ERROR: Missing argument for {}", all_arguments[argument]);
process::exit(1);
}
} else {
if !can_pass_argument {
println!("FATAL ERROR: Argument \"{}\" is not linked to any command", all_arguments[argument]);
process::exit(1);
} else {
can_pass_argument = false;
}
}
}
for a in &arguments {
println!("Argument number {} - {}", a.command, a.argument);
}
if commands_arguments.is_empty() { if commands_arguments.is_empty() {
println! {"Missing command, please read help for more info."}; println! {"FATAL ERROR: Missing type of app which you want to run, please read help for more info."};
process::exit(0); process::exit(0);
} }
match commands_arguments[0].as_ref() { match commands_arguments[0].as_ref() {
"-d" | "-duplicate_finder" => { "--d" => {
let delete_files: bool = commands_arguments.contains(&"-delete".to_owned()); let mut df = duplicate::DuplicateFinder::new();
if arguments.len() < 2 { if ArgumentsPair::has_command(&arguments, "-i") {
println!("FATAL ERROR: Duplicate Finder must be executed with at least 1 argument"); df.set_include_directory(ArgumentsPair::get_argument(&arguments, "-i"));
} else {
println!("FATAL ERROR: Parameter -i with set of included files is required.");
process::exit(1); process::exit(1);
} }
if ArgumentsPair::has_command(&arguments, "-e") {
let mut df = duplicate::DuplicateFinder::new(); df.set_exclude_directory(ArgumentsPair::get_argument(&arguments, "-e"));
df.set_include_directory(arguments[0].clone());
if arguments.len() > 1 {
df.set_exclude_directory(arguments[1].clone());
} }
if arguments.len() > 2 {
let min_size = match arguments[2].parse::<u64>() { if ArgumentsPair::has_command(&arguments, "-s") {
let min_size = match ArgumentsPair::get_argument(&arguments, "-s").parse::<u64>() {
Ok(t) => t, Ok(t) => t,
Err(_) => { Err(_) => {
println!("FATAL ERROR: \"{}\" is not valid file size(allowed range <0,u64::max>)", arguments[2]); println!("FATAL ERROR: \"{}\" is not valid file size(allowed range <0,u64::max>)", ArgumentsPair::get_argument(&arguments, "-s"));
process::exit(1); process::exit(1);
} }
}; };
df.set_min_file_size(min_size); df.set_min_file_size(min_size);
} }
if arguments.len() > 3 {
df.set_allowed_extensions(arguments[3].clone());
}
if arguments.len() > 4 {
df.set_excluded_items(arguments[4].clone());
}
df.find_duplicates(duplicate::CheckingMethod::SIZE, delete_files); if ArgumentsPair::has_command(&arguments, "-x") {
df.set_allowed_extensions(ArgumentsPair::get_argument(&arguments, "-x"));
}
if ArgumentsPair::has_command(&arguments, "-k") {
df.set_excluded_items(ArgumentsPair::get_argument(&arguments, "-k"));
}
if ArgumentsPair::has_command(&arguments, "-l") {
let check_method: duplicate::CheckingMethod;
if ArgumentsPair::get_argument(&arguments, "-l").to_lowercase() == "size" {
check_method = duplicate::CheckingMethod::SIZE;
} else if ArgumentsPair::get_argument(&arguments, "-l").to_lowercase() == "hash" {
check_method = duplicate::CheckingMethod::HASH;
} else {
println!("-l can only have values hash or size");
process::exit(1);
}
df.find_duplicates(check_method, ArgumentsPair::has_command(&arguments, "--delete"));
}
} }
"-h" | "-help" => { "--h" | "--help" => {
print_help(); print_help();
} }
argum => println!("\"{}\" argument is not supported, check help for more info.", argum), argum => {
println!("FATAL ERROR: \"{}\" argument is not supported, check help for more info.", argum);
process::exit(1);
}
}; };
} }
@ -78,11 +116,45 @@ fn print_help() {
println!("Usage of Czkawka:"); println!("Usage of Czkawka:");
println!("czkawka <option> <>"); println!("czkawka <option> <>");
println!("# Main arguments:"); println!("# Main arguments:");
println!(" -h - prints help, also works without any arguments"); println!(" --h / --help - prints help, also works without any arguments");
println!(" -help"); println!(" Usage example:");
println!(" -d <directory_to_search> [exclude_directories = \"\"] [min_size = 10] [allowed_extension = \"\"] [-delete] - search for duplicate files in choosen directories, minimum size(in bytes) and allowed extensions and avaibility to delete duplicates."); println!(" czkawka --help");
println!(" -duplicate_finder"); println!(" czkawka");
println!(" e.g."); println!(" --d - <-i directory_to_search> [-e exclude_directories = \"\"] [-s min_size = 1024] [-x allowed_extension = \"\"] [-l type_of_search = \"hash\"] [--delete] - search for duplicates files");
println!(" czkawka -d \"/home/rafal/,/home/szczekacz\" \"/home/rafal/Pulpit,/home/rafal/Obrazy\" 25 \"7z,rar,IMAGE\" -delete"); println!(" -i directory_to_search - list of directories which should will be searched like /home/rafal");
println!(" -e exclude_directories - list of directories which will be excluded from search.");
println!(" -s min_size - minimum size of checked files in bytes, assigning bigger value may speed up searching.");
println!(" -x allowed_extension - list of checked extension, e.g. \"jpg,mp4\" will allow to check \"book.jpg\" and \"car.mp4\" but not roman.png.There are also helpful macros which allow to easy use a typcal extension like IMAGE(\"jpg,kra,gif,png,bmp,tiff,webp,hdr,svg\") or TEXT(\"txt,doc,docx,odt,rtf\")");
println!(" -k type_of_search - allows to use fastest which takes into account only size, and more accurate which check if file contnet is same(hashes).");
println!(" --delete - removing file except one.");
println!(" Usage example:");
println!(" czkawka --d -i \"/home/rafal/,/home/szczekacz\" -e \"/home/rafal/Pulpit,/home/rafal/Obrazy\" -s 25 -x \"7z,rar,IMAGE\" -k \"size\" --delete");
println!(" czkawka --d -i \"/etc/,/mnt/Miecz\" -s 1000 -x \"VIDEO\" -k \"hash\"");
println!(" czkawka --d -i \"/etc/\" --delete");
println!(" --e");
println!(); println!();
} }
struct ArgumentsPair {
command: String,
argument: 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) -> String {
for a in ar {
if a.command == command {
return a.argument.clone();
}
}
panic!("FATAL ERROR: Get argument should always return value");
}
}