Removed not using packages, added better menu
This commit is contained in:
parent
0c1550d55e
commit
f9fa7d3879
195
Cargo.lock
generated
195
Cargo.lock
generated
|
@ -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"
|
||||||
|
|
|
@ -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"
|
|
12
README.md
12
README.md
|
@ -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)
|
||||||
|
|
119
src/duplicate.rs
119
src/duplicate.rs
|
@ -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() + ¤t_folder + &entry_data.file_name().into_string().unwrap();
|
let current_file_name = "".to_owned() + ¤t_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
|
|
||||||
// };
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
162
src/main.rs
162
src/main.rs
|
@ -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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue