diff --git a/Cargo.lock b/Cargo.lock index 288e476..a7363b4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -93,17 +93,6 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" -[[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 = "audio_checker" version = "0.1.0" @@ -347,26 +336,24 @@ dependencies = [ [[package]] name = "clap" -version = "3.2.23" +version = "4.0.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71655c45cb9845d3270c9d6df84ebe72b4dad3c2ba3f7023ad47c144e4e473a5" +checksum = "4d63b9e9c07271b9957ad22c173bae2a4d9a81127680962039296abcd2f8251d" dependencies = [ - "atty", "bitflags", "clap_derive", "clap_lex", - "indexmap", + "is-terminal", "once_cell", "strsim", "termcolor", - "textwrap", ] [[package]] name = "clap_derive" -version = "3.2.18" +version = "4.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea0c8bce528c4be4da13ea6fead8965e95b6073585a2f05204bd8f4119f82a65" +checksum = "0177313f9f02afc995627906bbd8967e2be069f5261954222dac78290c2b9014" dependencies = [ "heck", "proc-macro-error", @@ -377,9 +364,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.2.4" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" +checksum = "0d4198f73e42b4936b35b5bb248d81d2b595ecb170da0bac7655c54eedfa8da8" dependencies = [ "os_str_bytes", ] @@ -721,6 +708,27 @@ dependencies = [ "syn", ] +[[package]] +name = "errno" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" +dependencies = [ + "errno-dragonfly", + "libc", + "winapi", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + [[package]] name = "exr" version = "1.5.2" @@ -1324,6 +1332,15 @@ dependencies = [ "libc", ] +[[package]] +name = "hermit-abi" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +dependencies = [ + "libc", +] + [[package]] name = "hmac" version = "0.12.1" @@ -1553,6 +1570,28 @@ dependencies = [ "unic-langid", ] +[[package]] +name = "io-lifetimes" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46112a93252b123d31a119a8d1a1ac19deac4fac6e0e8b0df58f0d4e5870e63c" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "is-terminal" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "927609f78c2913a6f6ac3c27a4fe87f43e2a35367c0c4b0f8265e8f49a104330" +dependencies = [ + "hermit-abi 0.2.6", + "io-lifetimes", + "rustix", + "windows-sys", +] + [[package]] name = "itertools" version = "0.10.5" @@ -1650,6 +1689,12 @@ version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" +[[package]] +name = "linux-raw-sys" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" + [[package]] name = "locale_config" version = "0.3.0" @@ -1880,7 +1925,7 @@ version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6058e64324c71e02bc2b150e4f3bc8286db6c83092132ffa3f6b1eab0f9def5" dependencies = [ - "hermit-abi", + "hermit-abi 0.1.19", "libc", ] @@ -2447,6 +2492,20 @@ dependencies = [ "version_check", ] +[[package]] +name = "rustix" +version = "0.36.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3807b5d10909833d3e9acd1eb5fb988f79376ff10fce42937de71a449c4c588" +dependencies = [ + "bitflags", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys", + "windows-sys", +] + [[package]] name = "rustversion" version = "1.0.9" @@ -2910,12 +2969,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "textwrap" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" - [[package]] name = "thiserror" version = "1.0.37" diff --git a/czkawka_cli/Cargo.toml b/czkawka_cli/Cargo.toml index c718b3b..a45e5de 100644 --- a/czkawka_cli/Cargo.toml +++ b/czkawka_cli/Cargo.toml @@ -10,7 +10,7 @@ homepage = "https://github.com/qarmin/czkawka" repository = "https://github.com/qarmin/czkawka" [dependencies] -clap = { version = "3.2.23", features = ["derive"] } +clap = { version = "4.0.29", features = ["derive"] } # For enum types image_hasher = "1.1.2" diff --git a/czkawka_cli/src/commands.rs b/czkawka_cli/src/commands.rs index a570f4c..dca1cc0 100644 --- a/czkawka_cli/src/commands.rs +++ b/czkawka_cli/src/commands.rs @@ -8,269 +8,469 @@ use czkawka_core::same_music::MusicSimilarity; use czkawka_core::similar_images::SimilarityPreset; use czkawka_core::CZKAWKA_VERSION; -#[derive(Debug, clap::StructOpt)] -#[clap(name = "czkawka", help_message = HELP_MESSAGE, template = HELP_TEMPLATE, version = CZKAWKA_VERSION)] -pub enum Commands { - #[clap(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 hash -f results.txt -D aeo")] - Duplicates { - #[clap(flatten)] - directories: Directories, - #[clap(flatten)] - excluded_directories: ExcludedDirectories, - #[clap(flatten)] - excluded_items: ExcludedItems, - #[clap(short, long, parse(try_from_str = parse_minimal_file_size), default_value = "8192", help = "Minimum size in bytes", long_help = "Minimum size of checked files in bytes, assigning bigger value may speed up searching")] - minimal_file_size: u64, - #[clap(short = 'i', long, parse(try_from_str = parse_maximal_file_size), default_value = "18446744073709551615", help = "Maximum size in bytes", long_help = "Maximum size of checked files in bytes, assigning lower value may speed up searching")] - maximal_file_size: u64, - #[clap(short = 'c', long, parse(try_from_str = parse_minimal_file_size), default_value = "257144", help = "Minimum cached file size in bytes", long_help = "Minimum size of cached files in bytes, assigning bigger value may speed up the scan but loading the cache will be slower, assigning smaller value may slow down the scan and some files may need to be hashed again but loading the cache will be faster")] - minimal_cached_file_size: u64, - #[clap(flatten)] - allowed_extensions: AllowedExtensions, - #[clap(short, long, default_value = "HASH", parse(try_from_str = parse_checking_method), help = "Search method (NAME, SIZE, HASH)", long_help = "Methods to search files.\nNAME - Fast but but rarely usable,\nSIZE - Fast but not accurate, checking by the file's size,\nHASH - The slowest method, checking by the hash of the entire file")] - search_method: CheckingMethod, - #[clap(short = 'D', long, default_value = "NONE", parse(try_from_str = parse_delete_method), help = "Delete method (AEN, AEO, ON, OO, HARD)", 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\nHARD - create hard link\nNONE - not delete files")] - delete_method: DeleteMethod, - #[clap(short = 't', long, default_value = "BLAKE3", parse(try_from_str = parse_hash_type), help = "Hash type (BLAKE3, CRC32, XXH3)")] - hash_type: HashType, - #[clap(flatten)] - file_to_save: FileToSave, - #[clap(flatten)] - not_recursive: NotRecursive, - #[clap(flatten)] - case_sensitive_name_comparison: CaseSensitiveNameComparison, - #[cfg(target_family = "unix")] - #[clap(flatten)] - exclude_other_filesystems: ExcludeOtherFilesystems, - #[clap(flatten)] - allow_hard_links: AllowHardLinks, - #[clap(flatten)] - dryrun: DryRun, - }, - #[clap(name = "empty-folders", about = "Finds empty folders", help_message = HELP_MESSAGE, after_help = "EXAMPLE:\n czkawka empty-folders -d /home/rafal/rr /home/gateway -f results.txt")] - EmptyFolders { - #[clap(flatten)] - directories: Directories, - #[clap(flatten)] - excluded_directories: ExcludedDirectories, - #[clap(flatten)] - excluded_items: ExcludedItems, - #[clap(short = 'D', long, help = "Delete found folders")] - delete_folders: bool, - #[clap(flatten)] - file_to_save: FileToSave, - #[cfg(target_family = "unix")] - #[clap(flatten)] - exclude_other_filesystems: ExcludeOtherFilesystems, - }, - #[clap(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 -J -x VIDEO -f results.txt")] - BiggestFiles { - #[clap(flatten)] - directories: Directories, - #[clap(flatten)] - excluded_directories: ExcludedDirectories, - #[clap(flatten)] - excluded_items: ExcludedItems, - #[clap(flatten)] - allowed_extensions: AllowedExtensions, - #[clap(short, long, default_value = "50", help = "Number of files to be shown")] - number_of_files: usize, - #[clap(short = 'D', long, help = "Delete found files")] - delete_files: bool, - #[clap(flatten)] - file_to_save: FileToSave, - #[clap(flatten)] - not_recursive: NotRecursive, - #[clap(short = 'J', long, help = "Finds the smallest files instead the biggest")] - smallest_mode: bool, - #[cfg(target_family = "unix")] - #[clap(flatten)] - exclude_other_filesystems: ExcludeOtherFilesystems, - }, - #[clap(name = "empty-files", about = "Finds empty 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 { - #[clap(flatten)] - directories: Directories, - #[clap(flatten)] - excluded_directories: ExcludedDirectories, - #[clap(flatten)] - excluded_items: ExcludedItems, - #[clap(flatten)] - allowed_extensions: AllowedExtensions, - #[clap(short = 'D', long, help = "Delete found files")] - delete_files: bool, - #[clap(flatten)] - file_to_save: FileToSave, - #[clap(flatten)] - not_recursive: NotRecursive, - #[cfg(target_family = "unix")] - #[clap(flatten)] - exclude_other_filesystems: ExcludeOtherFilesystems, - }, - #[clap(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 { - #[clap(flatten)] - directories: Directories, - #[clap(flatten)] - excluded_directories: ExcludedDirectories, - #[clap(flatten)] - excluded_items: ExcludedItems, - #[clap(short = 'D', long, help = "Delete found files")] - delete_files: bool, - #[clap(flatten)] - file_to_save: FileToSave, - #[clap(flatten)] - not_recursive: NotRecursive, - #[cfg(target_family = "unix")] - #[clap(flatten)] - exclude_other_filesystems: ExcludeOtherFilesystems, - }, - #[clap(name = "image", about = "Finds similar images", help_message = HELP_MESSAGE, after_help = "EXAMPLE:\n czkawka image -d /home/rafal/ -E */.git */tmp* *Pulpit -f results.txt")] - SimilarImages { - #[clap(flatten)] - directories: Directories, - #[clap(flatten)] - excluded_directories: ExcludedDirectories, - #[clap(short, long, parse(try_from_str = parse_minimal_file_size), default_value = "16384", help = "Minimum size in bytes", long_help = "Minimum size of checked files in bytes, assigning bigger value may speed up searching")] - minimal_file_size: u64, - #[clap(short = 'i', long, parse(try_from_str = parse_maximal_file_size), default_value = "18446744073709551615", help = "Maximum size in bytes", long_help = "Maximum size of checked files in bytes, assigning lower value may speed up searching")] - maximal_file_size: u64, - #[clap(short, long, default_value = "High", parse(try_from_str = parse_similar_images_similarity), help = "Similairty level (Minimal, VerySmall, Small, Medium, High, VeryHigh, Original)", long_help = "Methods to choose similarity level of images which will be considered as duplicated.")] - similarity_preset: SimilarityPreset, - #[clap(flatten)] - excluded_items: ExcludedItems, - #[clap(flatten)] - file_to_save: FileToSave, - #[clap(flatten)] - not_recursive: NotRecursive, - #[cfg(target_family = "unix")] - #[clap(flatten)] - exclude_other_filesystems: ExcludeOtherFilesystems, - #[clap(short = 'g', long, default_value = "Gradient", parse(try_from_str = parse_similar_hash_algorithm), help = "Hash algorithm (allowed: Mean, Gradient, Blockhash, VertGradient, DoubleGradient)")] - hash_alg: HashAlg, - #[clap(short = 'z', long, default_value = "Nearest", parse(try_from_str = parse_similar_image_filter), help = "Hash algorithm (allowed: Lanczos3, Nearest, Triangle, Faussian, Catmullrom)")] - image_filter: FilterType, - #[clap(short = 'c', long, default_value = "16", parse(try_from_str = parse_image_hash_size), help = "Hash size (allowed: 8, 16, 32, 64)")] - hash_size: u8, - }, - #[clap(name = "music", about = "Finds same music by tags", help_message = HELP_MESSAGE, after_help = "EXAMPLE:\n czkawka music -d /home/rafal -f results.txt")] - SameMusic { - #[clap(flatten)] - directories: Directories, - #[clap(flatten)] - excluded_directories: ExcludedDirectories, - #[clap(flatten)] - excluded_items: ExcludedItems, - // #[clap(short = 'D', long, help = "Delete found files")] - // delete_files: bool, TODO - #[clap(short = 'z', long, default_value = "track_title,track_artist", parse(try_from_str = parse_music_duplicate_type), help = "Search method (track_title,track_artist,year,bitrate,genre,length))", long_help = "Sets which rows must be equal to set this files as duplicates(may be mixed, but must be divided by commas).")] - music_similarity: MusicSimilarity, - #[clap(flatten)] - file_to_save: FileToSave, - #[clap(flatten)] - not_recursive: NotRecursive, - #[cfg(target_family = "unix")] - #[clap(flatten)] - exclude_other_filesystems: ExcludeOtherFilesystems, - #[clap(short, long, parse(try_from_str = parse_minimal_file_size), default_value = "8192", help = "Minimum size in bytes", long_help = "Minimum size of checked files in bytes, assigning bigger value may speed up searching")] - minimal_file_size: u64, - #[clap(short = 'i', long, parse(try_from_str = parse_maximal_file_size), default_value = "18446744073709551615", help = "Maximum size in bytes", long_help = "Maximum size of checked files in bytes, assigning lower value may speed up searching")] - maximal_file_size: u64, - }, - #[clap(name = "symlinks", about = "Finds invalid symlinks", help_message = HELP_MESSAGE, after_help = "EXAMPLE:\n czkawka symlinks -d /home/kicikici/ /home/szczek -e /home/kicikici/jestempsem -x jpg -f results.txt")] - InvalidSymlinks { - #[clap(flatten)] - directories: Directories, - #[clap(flatten)] - excluded_directories: ExcludedDirectories, - #[clap(flatten)] - excluded_items: ExcludedItems, - #[clap(flatten)] - allowed_extensions: AllowedExtensions, - #[clap(short = 'D', long, help = "Delete found files")] - delete_files: bool, - #[clap(flatten)] - file_to_save: FileToSave, - #[clap(flatten)] - not_recursive: NotRecursive, - #[cfg(target_family = "unix")] - #[clap(flatten)] - exclude_other_filesystems: ExcludeOtherFilesystems, - }, - #[clap(name = "broken", about = "Finds broken files", help_message = HELP_MESSAGE, after_help = "EXAMPLE:\n czkawka broken -d /home/kicikici/ /home/szczek -e /home/kicikici/jestempsem -x jpg -f results.txt")] - BrokenFiles { - #[clap(flatten)] - directories: Directories, - #[clap(flatten)] - excluded_directories: ExcludedDirectories, - #[clap(flatten)] - excluded_items: ExcludedItems, - #[clap(flatten)] - allowed_extensions: AllowedExtensions, - #[clap(short = 'D', long, help = "Delete found files")] - delete_files: bool, - #[clap(flatten)] - file_to_save: FileToSave, - #[clap(flatten)] - not_recursive: NotRecursive, - #[cfg(target_family = "unix")] - #[clap(flatten)] - exclude_other_filesystems: ExcludeOtherFilesystems, - }, - #[clap(name = "video", about = "Finds similar video files", help_message = HELP_MESSAGE, after_help = "EXAMPLE:\n czkawka videos -d /home/rafal -f results.txt")] - SimilarVideos { - #[clap(flatten)] - directories: Directories, - #[clap(flatten)] - excluded_directories: ExcludedDirectories, - #[clap(flatten)] - excluded_items: ExcludedItems, - // #[clap(short = 'D', long, help = "Delete found files")] - // delete_files: bool, TODO - #[clap(flatten)] - file_to_save: FileToSave, - #[clap(flatten)] - allowed_extensions: AllowedExtensions, - #[clap(flatten)] - not_recursive: NotRecursive, - #[cfg(target_family = "unix")] - #[clap(flatten)] - exclude_other_filesystems: ExcludeOtherFilesystems, - #[clap(short, long, parse(try_from_str = parse_minimal_file_size), default_value = "8192", help = "Minimum size in bytes", long_help = "Minimum size of checked files in bytes, assigning bigger value may speed up searching")] - minimal_file_size: u64, - #[clap(short = 'i', long, parse(try_from_str = parse_maximal_file_size), default_value = "18446744073709551615", help = "Maximum size in bytes", long_help = "Maximum size of checked files in bytes, assigning lower value may speed up searching")] - maximal_file_size: u64, - #[clap(short = 't', long, parse(try_from_str = parse_tolerance), default_value = "10", help = "Video maximum difference (allowed values <0,20>)", long_help = "Maximum difference between video frames, bigger value means that videos can looks more and more different (allowed values <0,20>)")] - tolerance: i32, - }, - #[clap(name = "ext", about = "Finds files with invalid extensions", help_message = HELP_MESSAGE, after_help = "EXAMPLE:\n czkawka broken -d /home/czokolada/ -f results.txt")] - BadExtensions { - #[clap(flatten)] - directories: Directories, - #[clap(flatten)] - excluded_directories: ExcludedDirectories, - #[clap(flatten)] - excluded_items: ExcludedItems, - #[clap(flatten)] - allowed_extensions: AllowedExtensions, - #[clap(flatten)] - file_to_save: FileToSave, - #[clap(flatten)] - not_recursive: NotRecursive, - #[cfg(target_family = "unix")] - #[clap(flatten)] - exclude_other_filesystems: ExcludeOtherFilesystems, - }, - #[clap(name = "tester", about = "Small utility to test supported speed of ", help_message = HELP_MESSAGE, after_help = "EXAMPLE:\n czkawka tester")] - Tester {}, +#[derive(clap::Parser)] +#[clap( + name = "czkawka", + help_template = HELP_TEMPLATE, + version = CZKAWKA_VERSION +)] +pub struct Args { + #[command(subcommand)] + pub command: Commands, } -#[derive(Debug, clap::StructOpt)] +#[derive(Debug, clap::Subcommand)] +pub enum Commands { + #[clap( + name = "dup", + about = "Finds duplicate files", + after_help = "EXAMPLE:\n czkawka dup -d /home/rafal -e /home/rafal/Obrazy -m 25 -x 7z rar IMAGE -s hash -f results.txt -D aeo" + )] + Duplicates(DuplicatesArgs), + #[clap( + name = "empty-folders", + about = "Finds empty folders", + after_help = "EXAMPLE:\n czkawka empty-folders -d /home/rafal/rr /home/gateway -f results.txt" + )] + EmptyFolders(EmptyFoldersArgs), + #[clap( + name = "big", + about = "Finds big files", + after_help = "EXAMPLE:\n czkawka big -d /home/rafal/ /home/piszczal -e /home/rafal/Roman -n 25 -J -x VIDEO -f results.txt" + )] + BiggestFiles(BiggestFilesArgs), + #[clap( + name = "empty-files", + about = "Finds empty files", + after_help = "EXAMPLE:\n czkawka empty-files -d /home/rafal /home/szczekacz -e /home/rafal/Pulpit -R -f results.txt" + )] + EmptyFiles(EmptyFilesArgs), + #[clap( + name = "temp", + about = "Finds temporary files", + after_help = "EXAMPLE:\n czkawka temp -d /home/rafal/ -E */.git */tmp* *Pulpit -f results.txt -D" + )] + Temporary(TemporaryArgs), + #[clap( + name = "image", + about = "Finds similar images", + after_help = "EXAMPLE:\n czkawka image -d /home/rafal/ -E */.git */tmp* *Pulpit -f results.txt" + )] + SimilarImages(SimilarImagesArgs), + #[clap(name = "music", about = "Finds same music by tags", after_help = "EXAMPLE:\n czkawka music -d /home/rafal -f results.txt")] + SameMusic(SameMusicArgs), + #[clap( + name = "symlinks", + about = "Finds invalid symlinks", + after_help = "EXAMPLE:\n czkawka symlinks -d /home/kicikici/ /home/szczek -e /home/kicikici/jestempsem -x jpg -f results.txt" + )] + InvalidSymlinks(InvalidSymlinksArgs), + #[clap( + name = "broken", + about = "Finds broken files", + after_help = "EXAMPLE:\n czkawka broken -d /home/kicikici/ /home/szczek -e /home/kicikici/jestempsem -x jpg -f results.txt" + )] + BrokenFiles(BrokenFilesArgs), + #[clap(name = "video", about = "Finds similar video files", after_help = "EXAMPLE:\n czkawka videos -d /home/rafal -f results.txt")] + SimilarVideos(SimilarVideosArgs), + #[clap( + name = "ext", + about = "Finds files with invalid extensions", + after_help = "EXAMPLE:\n czkawka broken -d /home/czokolada/ -f results.txt" + )] + BadExtensions(BadExtensionsArgs), + #[clap(name = "tester", about = "Small utility to test supported speed of ", after_help = "EXAMPLE:\n czkawka tester")] + Tester, +} + +#[derive(Debug, clap::Args)] +pub struct DuplicatesArgs { + #[clap(flatten)] + pub directories: Directories, + #[clap(flatten)] + pub excluded_directories: ExcludedDirectories, + #[clap(flatten)] + pub excluded_items: ExcludedItems, + #[clap( + short, + long, + value_parser = parse_minimal_file_size, + default_value = "8192", + help = "Minimum size in bytes", + long_help = "Minimum size of checked files in bytes, assigning bigger value may speed up searching" + )] + pub minimal_file_size: u64, + #[clap( + short = 'i', + long, + value_parser = parse_maximal_file_size, + default_value = "18446744073709551615", + help = "Maximum size in bytes", + long_help = "Maximum size of checked files in bytes, assigning lower value may speed up searching" + )] + pub maximal_file_size: u64, + #[clap( + short = 'c', + long, + value_parser = parse_minimal_file_size, + default_value = "257144", + help = "Minimum cached file size in bytes", + long_help = "Minimum size of cached files in bytes, assigning bigger value may speed up the scan but loading the cache will be slower, assigning smaller value may slow down the scan and some files may need to be hashed again but loading the cache will be faster" + )] + pub minimal_cached_file_size: u64, + #[clap(flatten)] + pub allowed_extensions: AllowedExtensions, + #[clap( + short, + long, + default_value = "HASH", + value_parser = parse_checking_method, + help = "Search method (NAME, SIZE, HASH)", + long_help = "Methods to search files.\nNAME - Fast but but rarely usable,\nSIZE - Fast but not accurate, checking by the file's size,\nHASH - The slowest method, checking by the hash of the entire file" + )] + pub search_method: CheckingMethod, + #[clap( + short = 'D', + long, + default_value = "NONE", + value_parser = parse_delete_method, + help = "Delete method (AEN, AEO, ON, OO, HARD)", + 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\nHARD - create hard link\nNONE - not delete files" + )] + pub delete_method: DeleteMethod, + #[clap( + short = 't', + long, + default_value = "BLAKE3", + value_parser = parse_hash_type, + help = "Hash type (BLAKE3, CRC32, XXH3)" + )] + pub hash_type: HashType, + #[clap(flatten)] + pub file_to_save: FileToSave, + #[clap(flatten)] + pub not_recursive: NotRecursive, + #[clap(flatten)] + pub case_sensitive_name_comparison: CaseSensitiveNameComparison, + #[cfg(target_family = "unix")] + #[clap(flatten)] + pub exclude_other_filesystems: ExcludeOtherFilesystems, + #[clap(flatten)] + pub allow_hard_links: AllowHardLinks, + #[clap(flatten)] + pub dryrun: DryRun, +} + +#[derive(Debug, clap::Args)] +pub struct EmptyFoldersArgs { + #[clap(flatten)] + pub directories: Directories, + #[clap(flatten)] + pub excluded_directories: ExcludedDirectories, + #[clap(flatten)] + pub excluded_items: ExcludedItems, + #[clap(short = 'D', long, help = "Delete found folders")] + pub delete_folders: bool, + #[clap(flatten)] + pub file_to_save: FileToSave, + #[cfg(target_family = "unix")] + #[clap(flatten)] + pub exclude_other_filesystems: ExcludeOtherFilesystems, +} + +#[derive(Debug, clap::Args)] +pub struct BiggestFilesArgs { + #[clap(flatten)] + pub directories: Directories, + #[clap(flatten)] + pub excluded_directories: ExcludedDirectories, + #[clap(flatten)] + pub excluded_items: ExcludedItems, + #[clap(flatten)] + pub allowed_extensions: AllowedExtensions, + #[clap(short, long, default_value = "50", help = "Number of files to be shown")] + pub number_of_files: usize, + #[clap(short = 'D', long, help = "Delete found files")] + pub delete_files: bool, + #[clap(flatten)] + pub file_to_save: FileToSave, + #[clap(flatten)] + pub not_recursive: NotRecursive, + #[clap(short = 'J', long, help = "Finds the smallest files instead the biggest")] + pub smallest_mode: bool, + #[cfg(target_family = "unix")] + #[clap(flatten)] + pub exclude_other_filesystems: ExcludeOtherFilesystems, +} + +#[derive(Debug, clap::Args)] +pub struct EmptyFilesArgs { + #[clap(flatten)] + pub directories: Directories, + #[clap(flatten)] + pub excluded_directories: ExcludedDirectories, + #[clap(flatten)] + pub excluded_items: ExcludedItems, + #[clap(flatten)] + pub allowed_extensions: AllowedExtensions, + #[clap(short = 'D', long, help = "Delete found files")] + pub delete_files: bool, + #[clap(flatten)] + pub file_to_save: FileToSave, + #[clap(flatten)] + pub not_recursive: NotRecursive, + #[cfg(target_family = "unix")] + #[clap(flatten)] + pub exclude_other_filesystems: ExcludeOtherFilesystems, +} + +#[derive(Debug, clap::Args)] +pub struct TemporaryArgs { + #[clap(flatten)] + pub directories: Directories, + #[clap(flatten)] + pub excluded_directories: ExcludedDirectories, + #[clap(flatten)] + pub excluded_items: ExcludedItems, + #[clap(short = 'D', long, help = "Delete found files")] + pub delete_files: bool, + #[clap(flatten)] + pub file_to_save: FileToSave, + #[clap(flatten)] + pub not_recursive: NotRecursive, + #[cfg(target_family = "unix")] + #[clap(flatten)] + pub exclude_other_filesystems: ExcludeOtherFilesystems, +} + +#[derive(Debug, clap::Args)] +pub struct SimilarImagesArgs { + #[clap(flatten)] + pub directories: Directories, + #[clap(flatten)] + pub excluded_directories: ExcludedDirectories, + #[clap( + short, + long, + value_parser = parse_minimal_file_size, + default_value = "16384", + help = "Minimum size in bytes", + long_help = "Minimum size of checked files in bytes, assigning bigger value may speed up searching" + )] + pub minimal_file_size: u64, + #[clap( + short = 'i', + long, + value_parser = parse_minimal_file_size, + default_value = "18446744073709551615", + help = "Maximum size in bytes", + long_help = "Maximum size of checked files in bytes, assigning lower value may speed up searching" + )] + pub maximal_file_size: u64, + #[clap( + short, + long, + default_value = "High", + value_parser = parse_similar_images_similarity, + help = "Similairty level (Minimal, VerySmall, Small, Medium, High, VeryHigh, Original)", + long_help = "Methods to choose similarity level of images which will be considered as duplicated." + )] + pub similarity_preset: SimilarityPreset, + #[clap(flatten)] + pub excluded_items: ExcludedItems, + #[clap(flatten)] + pub file_to_save: FileToSave, + #[clap(flatten)] + pub not_recursive: NotRecursive, + #[cfg(target_family = "unix")] + #[clap(flatten)] + pub exclude_other_filesystems: ExcludeOtherFilesystems, + #[clap( + short = 'g', + long, + default_value = "Gradient", + value_parser = parse_similar_hash_algorithm, + help = "Hash algorithm (allowed: Mean, Gradient, Blockhash, VertGradient, DoubleGradient)" + )] + pub hash_alg: HashAlg, + #[clap( + short = 'z', + long, + default_value = "Nearest", + value_parser = parse_similar_image_filter, + help = "Hash algorithm (allowed: Lanczos3, Nearest, Triangle, Faussian, Catmullrom)" + )] + pub image_filter: FilterType, + #[clap( + short = 'c', + long, + default_value = "16", + value_parser = parse_image_hash_size, + help = "Hash size (allowed: 8, 16, 32, 64)" + )] + pub hash_size: u8, +} + +#[derive(Debug, clap::Args)] +pub struct SameMusicArgs { + #[clap(flatten)] + pub directories: Directories, + #[clap(flatten)] + pub excluded_directories: ExcludedDirectories, + #[clap(flatten)] + pub excluded_items: ExcludedItems, + // #[clap(short = 'D', long, help = "Delete found files")] + // delete_files: bool, TODO + #[clap( + short = 'z', + long, + default_value = "track_title,track_artist", + value_parser = parse_music_duplicate_type, + help = "Search method (track_title,track_artist,year,bitrate,genre,length))", + long_help = "Sets which rows must be equal to set this files as duplicates(may be mixed, but must be divided by commas)." + )] + pub music_similarity: MusicSimilarity, + #[clap(flatten)] + pub file_to_save: FileToSave, + #[clap(flatten)] + pub not_recursive: NotRecursive, + #[cfg(target_family = "unix")] + #[clap(flatten)] + pub exclude_other_filesystems: ExcludeOtherFilesystems, + #[clap( + short, + long, + value_parser = parse_minimal_file_size, + default_value = "8192", + help = "Minimum size in bytes", + long_help = "Minimum size of checked files in bytes, assigning bigger value may speed up searching" + )] + pub minimal_file_size: u64, + #[clap( + short = 'i', + long, + value_parser = parse_maximal_file_size, + default_value = "18446744073709551615", + help = "Maximum size in bytes", + long_help = "Maximum size of checked files in bytes, assigning lower value may speed up searching" + )] + pub maximal_file_size: u64, +} + +#[derive(Debug, clap::Args)] +pub struct InvalidSymlinksArgs { + #[clap(flatten)] + pub directories: Directories, + #[clap(flatten)] + pub excluded_directories: ExcludedDirectories, + #[clap(flatten)] + pub excluded_items: ExcludedItems, + #[clap(flatten)] + pub allowed_extensions: AllowedExtensions, + #[clap(short = 'D', long, help = "Delete found files")] + pub delete_files: bool, + #[clap(flatten)] + pub file_to_save: FileToSave, + #[clap(flatten)] + pub not_recursive: NotRecursive, + #[cfg(target_family = "unix")] + #[clap(flatten)] + pub exclude_other_filesystems: ExcludeOtherFilesystems, +} + +#[derive(Debug, clap::Args)] +pub struct BrokenFilesArgs { + #[clap(flatten)] + pub directories: Directories, + #[clap(flatten)] + pub excluded_directories: ExcludedDirectories, + #[clap(flatten)] + pub excluded_items: ExcludedItems, + #[clap(flatten)] + pub allowed_extensions: AllowedExtensions, + #[clap(short = 'D', long, help = "Delete found files")] + pub delete_files: bool, + #[clap(flatten)] + pub file_to_save: FileToSave, + #[clap(flatten)] + pub not_recursive: NotRecursive, + #[cfg(target_family = "unix")] + #[clap(flatten)] + pub exclude_other_filesystems: ExcludeOtherFilesystems, +} + +#[derive(Debug, clap::Args)] +pub struct SimilarVideosArgs { + #[clap(flatten)] + pub directories: Directories, + #[clap(flatten)] + pub excluded_directories: ExcludedDirectories, + #[clap(flatten)] + pub excluded_items: ExcludedItems, + // #[clap(short = 'D', long, help = "Delete found files")] + // delete_files: bool, TODO + #[clap(flatten)] + pub file_to_save: FileToSave, + #[clap(flatten)] + pub allowed_extensions: AllowedExtensions, + #[clap(flatten)] + pub not_recursive: NotRecursive, + #[cfg(target_family = "unix")] + #[clap(flatten)] + pub exclude_other_filesystems: ExcludeOtherFilesystems, + #[clap( + short, + long, + value_parser = parse_minimal_file_size, + default_value = "8192", + help = "Minimum size in bytes", + long_help = "Minimum size of checked files in bytes, assigning bigger value may speed up searching" + )] + pub minimal_file_size: u64, + #[clap( + short = 'i', + long, + value_parser = parse_maximal_file_size, + default_value = "18446744073709551615", + help = "Maximum size in bytes", + long_help = "Maximum size of checked files in bytes, assigning lower value may speed up searching" + )] + pub maximal_file_size: u64, + #[clap( + short = 't', + long, + value_parser = parse_tolerance, + default_value = "10", + help = "Video maximum difference (allowed values <0,20>)", + long_help = "Maximum difference between video frames, bigger value means that videos can looks more and more different (allowed values <0,20>)" + )] + pub tolerance: i32, +} + +#[derive(Debug, clap::Args)] +pub struct BadExtensionsArgs { + #[clap(flatten)] + pub directories: Directories, + #[clap(flatten)] + pub excluded_directories: ExcludedDirectories, + #[clap(flatten)] + pub excluded_items: ExcludedItems, + #[clap(flatten)] + pub allowed_extensions: AllowedExtensions, + #[clap(flatten)] + pub file_to_save: FileToSave, + #[clap(flatten)] + pub not_recursive: NotRecursive, + #[cfg(target_family = "unix")] + #[clap(flatten)] + pub exclude_other_filesystems: ExcludeOtherFilesystems, +} + +#[derive(Debug, clap::Args)] pub struct Directories { #[clap( 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)" @@ -278,19 +478,18 @@ pub struct Directories { pub directories: Vec, } -#[derive(Debug, clap::StructOpt)] +#[derive(Debug, clap::Args)] pub struct ExcludedDirectories { #[clap( short, long, - parse(from_os_str), help = "Excluded directorie(s)", long_help = "List of directorie(s) which will be excluded from search(absolute path)" )] pub excluded_directories: Vec, } -#[derive(Debug, clap::StructOpt)] +#[derive(Debug, clap::Args)] pub struct ExcludedItems { #[clap( short = 'E', @@ -301,7 +500,7 @@ pub struct ExcludedItems { pub excluded_items: Vec, } -#[derive(Debug, clap::StructOpt)] +#[derive(Debug, clap::Args)] pub struct AllowedExtensions { #[clap( short = 'x', @@ -312,38 +511,38 @@ pub struct AllowedExtensions { pub allowed_extensions: Vec, } -#[derive(Debug, clap::StructOpt)] +#[derive(Debug, clap::Args)] pub struct NotRecursive { #[clap(short = 'R', long, help = "Prevents from recursive check of folders")] pub not_recursive: bool, } #[cfg(target_family = "unix")] -#[derive(Debug, clap::StructOpt)] +#[derive(Debug, clap::Args)] pub struct ExcludeOtherFilesystems { #[clap(short = 'X', long, help = "Exclude files on other filesystems")] pub exclude_other_filesystems: bool, } -#[derive(Debug, clap::StructOpt)] +#[derive(Debug, clap::Args)] pub struct FileToSave { #[clap(short, long, value_name = "file-name", help = "Saves the results into the file")] pub file_to_save: Option, } -#[derive(Debug, clap::StructOpt)] +#[derive(Debug, clap::Args)] pub struct AllowHardLinks { #[clap(short = 'L', long, help = "Do not ignore hard links")] pub allow_hard_links: bool, } -#[derive(Debug, clap::StructOpt)] +#[derive(Debug, clap::Args)] pub struct CaseSensitiveNameComparison { #[clap(short = 'l', long, help = "Use case sensitive name comparison")] pub case_sensitive_name_comparison: bool, } -#[derive(Debug, clap::StructOpt)] +#[derive(Debug, clap::Args)] pub struct DryRun { #[clap(long, help = "Do nothing and print the operation that would happen.")] pub dryrun: bool, @@ -504,8 +703,6 @@ fn parse_music_duplicate_type(src: &str) -> Result { Ok(similarity) } -static HELP_MESSAGE: &str = "Prints help information (--help will give more information)"; - const HELP_TEMPLATE: &str = r#" {bin} {version} diff --git a/czkawka_cli/src/main.rs b/czkawka_cli/src/main.rs index d30a06c..7fa4faf 100644 --- a/czkawka_cli/src/main.rs +++ b/czkawka_cli/src/main.rs @@ -4,6 +4,10 @@ use std::process; use clap::Parser; +use crate::commands::{ + Args, BadExtensionsArgs, BiggestFilesArgs, BrokenFilesArgs, DuplicatesArgs, EmptyFilesArgs, EmptyFoldersArgs, InvalidSymlinksArgs, SameMusicArgs, SimilarImagesArgs, + SimilarVideosArgs, TemporaryArgs, +}; use commands::Commands; use czkawka_core::big_file::SearchMode; use czkawka_core::common::{get_number_of_threads, set_default_number_of_threads}; @@ -27,7 +31,7 @@ use czkawka_core::{ mod commands; fn main() { - let command = Commands::from_args(); + let command = Args::parse().command; set_default_number_of_threads(); println!("Set thread number to {}", get_number_of_threads()); @@ -35,440 +39,484 @@ fn main() { println!("{:?}", command); match command { - Commands::Duplicates { - directories, - excluded_directories, - excluded_items, - minimal_file_size, - maximal_file_size, - minimal_cached_file_size, - allowed_extensions, - search_method, - delete_method, - hash_type, - file_to_save, - not_recursive, - #[cfg(target_family = "unix")] - exclude_other_filesystems, - allow_hard_links, - dryrun, - case_sensitive_name_comparison, - } => { - let mut df = DuplicateFinder::new(); - - df.set_included_directory(directories.directories); - df.set_excluded_directory(excluded_directories.excluded_directories); - df.set_excluded_items(excluded_items.excluded_items); - df.set_minimal_file_size(minimal_file_size); - df.set_maximal_file_size(maximal_file_size); - df.set_minimal_cache_file_size(minimal_cached_file_size); - df.set_allowed_extensions(allowed_extensions.allowed_extensions.join(",")); - df.set_check_method(search_method); - df.set_delete_method(delete_method); - df.set_hash_type(hash_type); - df.set_recursive_search(!not_recursive.not_recursive); - #[cfg(target_family = "unix")] - df.set_exclude_other_filesystems(exclude_other_filesystems.exclude_other_filesystems); - df.set_ignore_hard_links(!allow_hard_links.allow_hard_links); - df.set_dryrun(dryrun.dryrun); - df.set_case_sensitive_name_comparison(case_sensitive_name_comparison.case_sensitive_name_comparison); - - df.find_duplicates(None, None); - - 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); - } - } - - #[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(); - } - Commands::EmptyFolders { - directories, - delete_folders, - file_to_save, - excluded_directories, - excluded_items, - #[cfg(target_family = "unix")] - exclude_other_filesystems, - } => { - let mut ef = EmptyFolder::new(); - - ef.set_included_directory(directories.directories); - ef.set_excluded_directory(excluded_directories.excluded_directories); - ef.set_excluded_items(excluded_items.excluded_items); - ef.set_delete_folder(delete_folders); - #[cfg(target_family = "unix")] - ef.set_exclude_other_filesystems(exclude_other_filesystems.exclude_other_filesystems); - - ef.find_empty_folders(None, None); - - 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 - ef.print_results(); - ef.get_text_messages().print_messages(); - } - Commands::BiggestFiles { - directories, - excluded_directories, - excluded_items, - allowed_extensions, - number_of_files, - file_to_save, - not_recursive, - #[cfg(target_family = "unix")] - exclude_other_filesystems, - delete_files, - smallest_mode, - } => { - let mut bf = BigFile::new(); - - bf.set_included_directory(directories.directories); - bf.set_excluded_directory(excluded_directories.excluded_directories); - bf.set_excluded_items(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); - #[cfg(target_family = "unix")] - bf.set_exclude_other_filesystems(exclude_other_filesystems.exclude_other_filesystems); - if delete_files { - bf.set_delete_method(big_file::DeleteMethod::Delete); - } - if smallest_mode { - bf.set_search_mode(SearchMode::SmallestFiles); - } - - bf.find_big_files(None, None); - - 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); - } - } - - #[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(); - } - Commands::EmptyFiles { - directories, - excluded_directories, - excluded_items, - allowed_extensions, - delete_files, - file_to_save, - not_recursive, - #[cfg(target_family = "unix")] - exclude_other_filesystems, - } => { - let mut ef = EmptyFiles::new(); - - ef.set_included_directory(directories.directories); - ef.set_excluded_directory(excluded_directories.excluded_directories); - ef.set_excluded_items(excluded_items.excluded_items); - ef.set_allowed_extensions(allowed_extensions.allowed_extensions.join(",")); - ef.set_recursive_search(!not_recursive.not_recursive); - #[cfg(target_family = "unix")] - ef.set_exclude_other_filesystems(exclude_other_filesystems.exclude_other_filesystems); - - if delete_files { - ef.set_delete_method(empty_files::DeleteMethod::Delete); - } - - ef.find_empty_files(None, None); - - 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 - ef.print_results(); - ef.get_text_messages().print_messages(); - } - Commands::Temporary { - directories, - excluded_directories, - excluded_items, - #[cfg(target_family = "unix")] - exclude_other_filesystems, - delete_files, - file_to_save, - not_recursive, - } => { - let mut tf = Temporary::new(); - - tf.set_included_directory(directories.directories); - tf.set_excluded_directory(excluded_directories.excluded_directories); - tf.set_excluded_items(excluded_items.excluded_items); - tf.set_recursive_search(!not_recursive.not_recursive); - #[cfg(target_family = "unix")] - tf.set_exclude_other_filesystems(exclude_other_filesystems.exclude_other_filesystems); - - if delete_files { - tf.set_delete_method(temporary::DeleteMethod::Delete); - } - - tf.find_temporary_files(None, None); - - 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); - } - } - - #[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(); - } - Commands::SimilarImages { - directories, - excluded_directories, - excluded_items, - file_to_save, - minimal_file_size, - maximal_file_size, - similarity_preset, - not_recursive, - #[cfg(target_family = "unix")] - exclude_other_filesystems, - hash_alg, - image_filter, - hash_size, - } => { - let mut sf = SimilarImages::new(); - - sf.set_included_directory(directories.directories); - sf.set_excluded_directory(excluded_directories.excluded_directories); - sf.set_excluded_items(excluded_items.excluded_items); - sf.set_minimal_file_size(minimal_file_size); - sf.set_maximal_file_size(maximal_file_size); - sf.set_recursive_search(!not_recursive.not_recursive); - #[cfg(target_family = "unix")] - sf.set_exclude_other_filesystems(exclude_other_filesystems.exclude_other_filesystems); - sf.set_image_filter(image_filter); - sf.set_hash_alg(hash_alg); - sf.set_hash_size(hash_size); - - sf.set_similarity(return_similarity_from_similarity_preset(&similarity_preset, hash_size)); - - sf.find_similar_images(None, None); - - if let Some(file_name) = file_to_save.file_name() { - if !sf.save_results_to_file(file_name) { - sf.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 - sf.print_results(); - sf.get_text_messages().print_messages(); - } - Commands::SameMusic { - directories, - excluded_directories, - excluded_items, - // delete_files, - file_to_save, - not_recursive, - #[cfg(target_family = "unix")] - exclude_other_filesystems, - minimal_file_size, - maximal_file_size, - music_similarity, - } => { - let mut mf = SameMusic::new(); - - mf.set_included_directory(directories.directories); - mf.set_excluded_directory(excluded_directories.excluded_directories); - mf.set_excluded_items(excluded_items.excluded_items); - mf.set_minimal_file_size(minimal_file_size); - mf.set_maximal_file_size(maximal_file_size); - mf.set_recursive_search(!not_recursive.not_recursive); - #[cfg(target_family = "unix")] - mf.set_exclude_other_filesystems(exclude_other_filesystems.exclude_other_filesystems); - mf.set_music_similarity(music_similarity); - - // if delete_files { - // // TODO mf.set_delete_method(same_music::DeleteMethod::Delete); - // } - - mf.find_same_music(None, None); - - if let Some(file_name) = file_to_save.file_name() { - if !mf.save_results_to_file(file_name) { - mf.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 - mf.print_results(); - mf.get_text_messages().print_messages(); - } - Commands::InvalidSymlinks { - directories, - excluded_directories, - excluded_items, - allowed_extensions, - file_to_save, - not_recursive, - #[cfg(target_family = "unix")] - exclude_other_filesystems, - delete_files, - } => { - let mut ifs = InvalidSymlinks::new(); - - ifs.set_included_directory(directories.directories); - ifs.set_excluded_directory(excluded_directories.excluded_directories); - ifs.set_excluded_items(excluded_items.excluded_items); - ifs.set_allowed_extensions(allowed_extensions.allowed_extensions.join(",")); - ifs.set_recursive_search(!not_recursive.not_recursive); - #[cfg(target_family = "unix")] - ifs.set_exclude_other_filesystems(exclude_other_filesystems.exclude_other_filesystems); - if delete_files { - ifs.set_delete_method(invalid_symlinks::DeleteMethod::Delete); - } - - ifs.find_invalid_links(None, None); - - if let Some(file_name) = file_to_save.file_name() { - if !ifs.save_results_to_file(file_name) { - ifs.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 - ifs.print_results(); - ifs.get_text_messages().print_messages(); - } - Commands::BrokenFiles { - directories, - excluded_directories, - excluded_items, - allowed_extensions, - delete_files, - file_to_save, - not_recursive, - #[cfg(target_family = "unix")] - exclude_other_filesystems, - } => { - let mut br = BrokenFiles::new(); - - br.set_included_directory(directories.directories); - br.set_excluded_directory(excluded_directories.excluded_directories); - br.set_excluded_items(excluded_items.excluded_items); - br.set_allowed_extensions(allowed_extensions.allowed_extensions.join(",")); - br.set_recursive_search(!not_recursive.not_recursive); - #[cfg(target_family = "unix")] - br.set_exclude_other_filesystems(exclude_other_filesystems.exclude_other_filesystems); - - if delete_files { - br.set_delete_method(broken_files::DeleteMethod::Delete); - } - - br.find_broken_files(None, None); - - if let Some(file_name) = file_to_save.file_name() { - if !br.save_results_to_file(file_name) { - br.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 - br.print_results(); - br.get_text_messages().print_messages(); - } - Commands::SimilarVideos { - directories, - excluded_directories, - excluded_items, - file_to_save, - not_recursive, - #[cfg(target_family = "unix")] - exclude_other_filesystems, - tolerance, - minimal_file_size, - maximal_file_size, - allowed_extensions, - } => { - let mut vr = SimilarVideos::new(); - - vr.set_included_directory(directories.directories); - vr.set_excluded_directory(excluded_directories.excluded_directories); - vr.set_excluded_items(excluded_items.excluded_items); - vr.set_allowed_extensions(allowed_extensions.allowed_extensions.join(",")); - vr.set_recursive_search(!not_recursive.not_recursive); - #[cfg(target_family = "unix")] - vr.set_exclude_other_filesystems(exclude_other_filesystems.exclude_other_filesystems); - vr.set_minimal_file_size(minimal_file_size); - vr.set_maximal_file_size(maximal_file_size); - vr.set_tolerance(tolerance); - - vr.find_similar_videos(None, None); - - if let Some(file_name) = file_to_save.file_name() { - if !vr.save_results_to_file(file_name) { - vr.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 - vr.print_results(); - vr.get_text_messages().print_messages(); - } - Commands::BadExtensions { - directories, - excluded_directories, - excluded_items, - file_to_save, - not_recursive, - #[cfg(target_family = "unix")] - exclude_other_filesystems, - allowed_extensions, - } => { - let mut be = BadExtensions::new(); - - be.set_included_directory(directories.directories); - be.set_excluded_directory(excluded_directories.excluded_directories); - be.set_excluded_items(excluded_items.excluded_items); - be.set_allowed_extensions(allowed_extensions.allowed_extensions.join(",")); - be.set_recursive_search(!not_recursive.not_recursive); - #[cfg(target_family = "unix")] - be.set_exclude_other_filesystems(exclude_other_filesystems.exclude_other_filesystems); - - if let Some(file_name) = file_to_save.file_name() { - if !be.save_results_to_file(file_name) { - be.get_text_messages().print_messages(); - process::exit(1); - } - } - - be.find_bad_extensions_files(None, None); - - #[cfg(not(debug_assertions))] // This will show too much probably unnecessary data to debug, comment line only if needed - be.print_results(); - be.get_text_messages().print_messages(); - } + Commands::Duplicates(duplicates_args) => duplicates(duplicates_args), + Commands::EmptyFolders(empty_folders_args) => empty_folders(empty_folders_args), + Commands::BiggestFiles(biggest_files_args) => biggest_files(biggest_files_args), + Commands::EmptyFiles(empty_files_args) => empty_files(empty_files_args), + Commands::Temporary(temporary_args) => temporary(temporary_args), + Commands::SimilarImages(similar_images_args) => similar_images(similar_images_args), + Commands::SameMusic(same_music_args) => same_music(same_music_args), + Commands::InvalidSymlinks(invalid_symlinks_args) => invalid_symlinks(invalid_symlinks_args), + Commands::BrokenFiles(broken_files_args) => broken_files(broken_files_args), + Commands::SimilarVideos(similar_videos_args) => similar_videos(similar_videos_args), + Commands::BadExtensions(bad_extensions_args) => bad_extensions(bad_extensions_args), Commands::Tester {} => { test_image_conversion_speed(); } } } + +fn duplicates(duplicates: DuplicatesArgs) { + let DuplicatesArgs { + directories, + excluded_directories, + excluded_items, + minimal_file_size, + maximal_file_size, + minimal_cached_file_size, + allowed_extensions, + search_method, + delete_method, + hash_type, + file_to_save, + not_recursive, + #[cfg(target_family = "unix")] + exclude_other_filesystems, + allow_hard_links, + dryrun, + case_sensitive_name_comparison, + } = duplicates; + + let mut df = DuplicateFinder::new(); + + df.set_included_directory(directories.directories); + df.set_excluded_directory(excluded_directories.excluded_directories); + df.set_excluded_items(excluded_items.excluded_items); + df.set_minimal_file_size(minimal_file_size); + df.set_maximal_file_size(maximal_file_size); + df.set_minimal_cache_file_size(minimal_cached_file_size); + df.set_allowed_extensions(allowed_extensions.allowed_extensions.join(",")); + df.set_check_method(search_method); + df.set_delete_method(delete_method); + df.set_hash_type(hash_type); + df.set_recursive_search(!not_recursive.not_recursive); + #[cfg(target_family = "unix")] + df.set_exclude_other_filesystems(exclude_other_filesystems.exclude_other_filesystems); + df.set_ignore_hard_links(!allow_hard_links.allow_hard_links); + df.set_dryrun(dryrun.dryrun); + df.set_case_sensitive_name_comparison(case_sensitive_name_comparison.case_sensitive_name_comparison); + + df.find_duplicates(None, None); + + 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); + } + } + + #[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(); +} + +fn empty_folders(empty_folders: EmptyFoldersArgs) { + let EmptyFoldersArgs { + directories, + delete_folders, + file_to_save, + excluded_directories, + excluded_items, + #[cfg(target_family = "unix")] + exclude_other_filesystems, + } = empty_folders; + + let mut ef = EmptyFolder::new(); + + ef.set_included_directory(directories.directories); + ef.set_excluded_directory(excluded_directories.excluded_directories); + ef.set_excluded_items(excluded_items.excluded_items); + ef.set_delete_folder(delete_folders); + #[cfg(target_family = "unix")] + ef.set_exclude_other_filesystems(exclude_other_filesystems.exclude_other_filesystems); + + ef.find_empty_folders(None, None); + + 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 + ef.print_results(); + ef.get_text_messages().print_messages(); +} + +fn biggest_files(biggest_files: BiggestFilesArgs) { + let BiggestFilesArgs { + directories, + excluded_directories, + excluded_items, + allowed_extensions, + number_of_files, + file_to_save, + not_recursive, + #[cfg(target_family = "unix")] + exclude_other_filesystems, + delete_files, + smallest_mode, + } = biggest_files; + + let mut bf = BigFile::new(); + + bf.set_included_directory(directories.directories); + bf.set_excluded_directory(excluded_directories.excluded_directories); + bf.set_excluded_items(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); + #[cfg(target_family = "unix")] + bf.set_exclude_other_filesystems(exclude_other_filesystems.exclude_other_filesystems); + if delete_files { + bf.set_delete_method(big_file::DeleteMethod::Delete); + } + if smallest_mode { + bf.set_search_mode(SearchMode::SmallestFiles); + } + + bf.find_big_files(None, None); + + 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); + } + } + + #[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(); +} + +fn empty_files(empty_files: EmptyFilesArgs) { + let EmptyFilesArgs { + directories, + excluded_directories, + excluded_items, + allowed_extensions, + delete_files, + file_to_save, + not_recursive, + #[cfg(target_family = "unix")] + exclude_other_filesystems, + } = empty_files; + + let mut ef = EmptyFiles::new(); + + ef.set_included_directory(directories.directories); + ef.set_excluded_directory(excluded_directories.excluded_directories); + ef.set_excluded_items(excluded_items.excluded_items); + ef.set_allowed_extensions(allowed_extensions.allowed_extensions.join(",")); + ef.set_recursive_search(!not_recursive.not_recursive); + #[cfg(target_family = "unix")] + ef.set_exclude_other_filesystems(exclude_other_filesystems.exclude_other_filesystems); + + if delete_files { + ef.set_delete_method(empty_files::DeleteMethod::Delete); + } + + ef.find_empty_files(None, None); + + 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 + ef.print_results(); + ef.get_text_messages().print_messages(); +} + +fn temporary(temporary: TemporaryArgs) { + let TemporaryArgs { + directories, + excluded_directories, + excluded_items, + #[cfg(target_family = "unix")] + exclude_other_filesystems, + delete_files, + file_to_save, + not_recursive, + } = temporary; + + let mut tf = Temporary::new(); + + tf.set_included_directory(directories.directories); + tf.set_excluded_directory(excluded_directories.excluded_directories); + tf.set_excluded_items(excluded_items.excluded_items); + tf.set_recursive_search(!not_recursive.not_recursive); + #[cfg(target_family = "unix")] + tf.set_exclude_other_filesystems(exclude_other_filesystems.exclude_other_filesystems); + + if delete_files { + tf.set_delete_method(temporary::DeleteMethod::Delete); + } + + tf.find_temporary_files(None, None); + + 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); + } + } + + #[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(); +} + +fn similar_images(similar_images: SimilarImagesArgs) { + let SimilarImagesArgs { + directories, + excluded_directories, + excluded_items, + file_to_save, + minimal_file_size, + maximal_file_size, + similarity_preset, + not_recursive, + #[cfg(target_family = "unix")] + exclude_other_filesystems, + hash_alg, + image_filter, + hash_size, + } = similar_images; + + let mut sf = SimilarImages::new(); + + sf.set_included_directory(directories.directories); + sf.set_excluded_directory(excluded_directories.excluded_directories); + sf.set_excluded_items(excluded_items.excluded_items); + sf.set_minimal_file_size(minimal_file_size); + sf.set_maximal_file_size(maximal_file_size); + sf.set_recursive_search(!not_recursive.not_recursive); + #[cfg(target_family = "unix")] + sf.set_exclude_other_filesystems(exclude_other_filesystems.exclude_other_filesystems); + sf.set_image_filter(image_filter); + sf.set_hash_alg(hash_alg); + sf.set_hash_size(hash_size); + + sf.set_similarity(return_similarity_from_similarity_preset(&similarity_preset, hash_size)); + + sf.find_similar_images(None, None); + + if let Some(file_name) = file_to_save.file_name() { + if !sf.save_results_to_file(file_name) { + sf.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 + sf.print_results(); + sf.get_text_messages().print_messages(); +} + +fn same_music(same_music: SameMusicArgs) { + let SameMusicArgs { + directories, + excluded_directories, + excluded_items, + // delete_files, + file_to_save, + not_recursive, + #[cfg(target_family = "unix")] + exclude_other_filesystems, + minimal_file_size, + maximal_file_size, + music_similarity, + } = same_music; + + let mut mf = SameMusic::new(); + + mf.set_included_directory(directories.directories); + mf.set_excluded_directory(excluded_directories.excluded_directories); + mf.set_excluded_items(excluded_items.excluded_items); + mf.set_minimal_file_size(minimal_file_size); + mf.set_maximal_file_size(maximal_file_size); + mf.set_recursive_search(!not_recursive.not_recursive); + #[cfg(target_family = "unix")] + mf.set_exclude_other_filesystems(exclude_other_filesystems.exclude_other_filesystems); + mf.set_music_similarity(music_similarity); + + // if delete_files { + // // TODO mf.set_delete_method(same_music::DeleteMethod::Delete); + // } + + mf.find_same_music(None, None); + + if let Some(file_name) = file_to_save.file_name() { + if !mf.save_results_to_file(file_name) { + mf.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 + mf.print_results(); + mf.get_text_messages().print_messages(); +} + +fn invalid_symlinks(invalid_symlinks: InvalidSymlinksArgs) { + let InvalidSymlinksArgs { + directories, + excluded_directories, + excluded_items, + allowed_extensions, + file_to_save, + not_recursive, + #[cfg(target_family = "unix")] + exclude_other_filesystems, + delete_files, + } = invalid_symlinks; + + let mut ifs = InvalidSymlinks::new(); + + ifs.set_included_directory(directories.directories); + ifs.set_excluded_directory(excluded_directories.excluded_directories); + ifs.set_excluded_items(excluded_items.excluded_items); + ifs.set_allowed_extensions(allowed_extensions.allowed_extensions.join(",")); + ifs.set_recursive_search(!not_recursive.not_recursive); + #[cfg(target_family = "unix")] + ifs.set_exclude_other_filesystems(exclude_other_filesystems.exclude_other_filesystems); + if delete_files { + ifs.set_delete_method(invalid_symlinks::DeleteMethod::Delete); + } + + ifs.find_invalid_links(None, None); + + if let Some(file_name) = file_to_save.file_name() { + if !ifs.save_results_to_file(file_name) { + ifs.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 + ifs.print_results(); + ifs.get_text_messages().print_messages(); +} + +fn broken_files(broken_files: BrokenFilesArgs) { + let BrokenFilesArgs { + directories, + excluded_directories, + excluded_items, + allowed_extensions, + delete_files, + file_to_save, + not_recursive, + #[cfg(target_family = "unix")] + exclude_other_filesystems, + } = broken_files; + + let mut br = BrokenFiles::new(); + + br.set_included_directory(directories.directories); + br.set_excluded_directory(excluded_directories.excluded_directories); + br.set_excluded_items(excluded_items.excluded_items); + br.set_allowed_extensions(allowed_extensions.allowed_extensions.join(",")); + br.set_recursive_search(!not_recursive.not_recursive); + #[cfg(target_family = "unix")] + br.set_exclude_other_filesystems(exclude_other_filesystems.exclude_other_filesystems); + + if delete_files { + br.set_delete_method(broken_files::DeleteMethod::Delete); + } + + br.find_broken_files(None, None); + + if let Some(file_name) = file_to_save.file_name() { + if !br.save_results_to_file(file_name) { + br.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 + br.print_results(); + br.get_text_messages().print_messages(); +} + +fn similar_videos(similar_videos: SimilarVideosArgs) { + let SimilarVideosArgs { + directories, + excluded_directories, + excluded_items, + file_to_save, + not_recursive, + #[cfg(target_family = "unix")] + exclude_other_filesystems, + tolerance, + minimal_file_size, + maximal_file_size, + allowed_extensions, + } = similar_videos; + + let mut vr = SimilarVideos::new(); + + vr.set_included_directory(directories.directories); + vr.set_excluded_directory(excluded_directories.excluded_directories); + vr.set_excluded_items(excluded_items.excluded_items); + vr.set_allowed_extensions(allowed_extensions.allowed_extensions.join(",")); + vr.set_recursive_search(!not_recursive.not_recursive); + #[cfg(target_family = "unix")] + vr.set_exclude_other_filesystems(exclude_other_filesystems.exclude_other_filesystems); + vr.set_minimal_file_size(minimal_file_size); + vr.set_maximal_file_size(maximal_file_size); + vr.set_tolerance(tolerance); + + vr.find_similar_videos(None, None); + + if let Some(file_name) = file_to_save.file_name() { + if !vr.save_results_to_file(file_name) { + vr.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 + vr.print_results(); + vr.get_text_messages().print_messages(); +} + +fn bad_extensions(bad_extensions: BadExtensionsArgs) { + let BadExtensionsArgs { + directories, + excluded_directories, + excluded_items, + file_to_save, + not_recursive, + #[cfg(target_family = "unix")] + exclude_other_filesystems, + allowed_extensions, + } = bad_extensions; + + let mut be = BadExtensions::new(); + + be.set_included_directory(directories.directories); + be.set_excluded_directory(excluded_directories.excluded_directories); + be.set_excluded_items(excluded_items.excluded_items); + be.set_allowed_extensions(allowed_extensions.allowed_extensions.join(",")); + be.set_recursive_search(!not_recursive.not_recursive); + #[cfg(target_family = "unix")] + be.set_exclude_other_filesystems(exclude_other_filesystems.exclude_other_filesystems); + + if let Some(file_name) = file_to_save.file_name() { + if !be.save_results_to_file(file_name) { + be.get_text_messages().print_messages(); + process::exit(1); + } + } + + be.find_bad_extensions_files(None, None); + + #[cfg(not(debug_assertions))] // This will show too much probably unnecessary data to debug, comment line only if needed + be.print_results(); + be.get_text_messages().print_messages(); +}