From 219f9f058b76a74f18ffccd85b3b1bf94b18dadc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mikrut?= <41945903+qarmin@users.noreply.github.com> Date: Mon, 11 Dec 2023 19:12:26 +0100 Subject: [PATCH] Allocation decrease (#1156) --- Cargo.lock | 243 +++++++++++++---------- ci_tester/src/main.rs | 19 +- czkawka_cli/Cargo.toml | 4 +- czkawka_core/Cargo.toml | 18 +- czkawka_core/build.rs | 18 ++ czkawka_core/src/bad_extensions.rs | 4 +- czkawka_core/src/big_file.rs | 16 +- czkawka_core/src/broken_files.rs | 7 +- czkawka_core/src/common.rs | 26 ++- czkawka_core/src/common_dir_traversal.rs | 195 ++---------------- czkawka_core/src/duplicate.rs | 11 +- czkawka_core/src/empty_files.rs | 4 +- czkawka_core/src/empty_folder.rs | 215 +++++++++++++++++--- czkawka_core/src/invalid_symlinks.rs | 1 - czkawka_core/src/same_music.rs | 4 +- czkawka_core/src/similar_images.rs | 12 +- czkawka_core/src/similar_videos.rs | 7 +- czkawka_core/src/temporary.rs | 9 +- czkawka_gui/Cargo.toml | 10 +- czkawka_gui/src/compute_results.rs | 6 +- czkawka_gui/src/main.rs | 2 + czkawka_gui/src/saving_loading.rs | 2 +- krokiet/Cargo.toml | 4 +- krokiet/src/connect_scan.rs | 3 +- krokiet/src/main.rs | 12 ++ 25 files changed, 453 insertions(+), 399 deletions(-) create mode 100644 czkawka_core/build.rs diff --git a/Cargo.lock b/Cargo.lock index ccfd7cc..1a87015 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -113,9 +113,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.4" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44" +checksum = "d664a92ecae85fd0a7392615844904654d1d5f5514837f471ddef4a057aba1b6" dependencies = [ "anstyle", "anstyle-parse", @@ -133,30 +133,30 @@ checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" [[package]] name = "anstyle-parse" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140" +checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.0.0" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.1" +version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" +checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" dependencies = [ "anstyle", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -324,7 +324,7 @@ dependencies = [ "futures-lite 2.1.0", "parking", "polling 3.3.1", - "rustix 0.38.26", + "rustix 0.38.28", "slab", "tracing", "windows-sys 0.52.0", @@ -363,7 +363,7 @@ dependencies = [ "cfg-if", "event-listener 3.1.0", "futures-lite 1.13.0", - "rustix 0.38.26", + "rustix 0.38.28", "windows-sys 0.48.0", ] @@ -390,7 +390,7 @@ dependencies = [ "cfg-if", "futures-core", "futures-io", - "rustix 0.38.26", + "rustix 0.38.28", "signal-hook-registry", "slab", "windows-sys 0.48.0", @@ -439,15 +439,6 @@ dependencies = [ "syn 2.0.39", ] -[[package]] -name = "atomic-polyfill" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4" -dependencies = [ - "critical-section", -] - [[package]] name = "atomic-waker" version = "1.1.2" @@ -724,7 +715,7 @@ dependencies = [ "bitflags 2.4.1", "log", "polling 3.3.1", - "rustix 0.38.26", + "rustix 0.38.28", "slab", "thiserror", ] @@ -736,7 +727,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0f0ea9b9476c7fad82841a8dbb380e2eae480c21910feba80725b46931ed8f02" dependencies = [ "calloop", - "rustix 0.38.26", + "rustix 0.38.28", "wayland-backend", "wayland-client", ] @@ -854,9 +845,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.4.10" +version = "4.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41fffed7514f420abec6d183b1d3acfd9099c79c3a10a06ade4f8203f1411272" +checksum = "bfaff671f6b22ca62406885ece523383b9b64022e341e53e009a62ebc47a45f2" dependencies = [ "clap_builder", "clap_derive", @@ -864,9 +855,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.4.9" +version = "4.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63361bae7eef3771745f02d8d892bec2fee5f6e34af316ba556e7f97a7069ff1" +checksum = "a216b506622bb1d316cd51328dce24e07bdff4a6128a47c7e7fad11878d5adbb" dependencies = [ "anstream", "anstyle", @@ -1096,6 +1087,15 @@ dependencies = [ "libc", ] +[[package]] +name = "core2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505" +dependencies = [ + "memchr", +] + [[package]] name = "countme" version = "3.0.1" @@ -1343,6 +1343,12 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "dary_heap" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7762d17f1241643615821a8455a0b2c3e803784b058693d990b11f2dce25a0ca" + [[package]] name = "dashmap" version = "5.5.3" @@ -1803,14 +1809,14 @@ dependencies = [ [[package]] name = "filetime" -version = "0.2.22" +version = "0.2.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4029edd3e734da6fe05b6cd7bd2960760a616bd2ddd0d59a0124746d6272af0" +checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.3.5", - "windows-sys 0.48.0", + "redox_syscall 0.4.1", + "windows-sys 0.52.0", ] [[package]] @@ -2258,9 +2264,9 @@ dependencies = [ [[package]] name = "gio" -version = "0.18.3" +version = "0.18.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47d809baf02bdf1b5ef4ad3bf60dd9d4977149db4612b7bbb58e56aef168193b" +checksum = "d4fc8f532f87b79cbc51a79748f16a6828fb784be93145a322fa14d06d354c73" dependencies = [ "futures-channel", "futures-core", @@ -2301,9 +2307,9 @@ dependencies = [ [[package]] name = "glib" -version = "0.18.3" +version = "0.18.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58cf801b6f7829fa76db37449ab67c9c98a2b1bf21076d9113225621e61a0fa6" +checksum = "951bbd7fdc5c044ede9f05170f05a3ae9479239c3afdfe2d22d537a3add15c4e" dependencies = [ "bitflags 2.4.1", "futures-channel", @@ -2605,6 +2611,15 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash", +] + [[package]] name = "hashbrown" version = "0.14.3" @@ -3111,15 +3126,6 @@ dependencies = [ "cfb", ] -[[package]] -name = "inflate" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cdb29978cc5797bd8dcc8e5bf7de604891df2a8dc576973d71a281e916db2ff" -dependencies = [ - "adler32", -] - [[package]] name = "inout" version = "0.1.3" @@ -3249,9 +3255,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" [[package]] name = "jni" @@ -3384,6 +3390,30 @@ version = "0.2.150" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" +[[package]] +name = "libflate" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7d5654ae1795afc7ff76f4365c2c8791b0feb18e8996a96adad8ffd7c3b2bf" +dependencies = [ + "adler32", + "core2", + "crc32fast", + "dary_heap", + "libflate_lz77", +] + +[[package]] +name = "libflate_lz77" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be5f52fb8c451576ec6b79d3f4deb327398bc05bbdbd99021a6e77a4c855d524" +dependencies = [ + "core2", + "hashbrown 0.13.2", + "rle-decode-fast", +] + [[package]] name = "libheif-rs" version = "0.18.0" @@ -3553,9 +3583,9 @@ dependencies = [ [[package]] name = "lofty" -version = "0.16.1" +version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c18ba58211b3c3557970755d7afc3a7438c2d4557bcd684470b2195c0ae66e53" +checksum = "e9d514acc39ec2bee7392e451db35e5f5eb06ab26e1cae83746c3017e8c95099" dependencies = [ "base64", "byteorder", @@ -3955,9 +3985,9 @@ dependencies = [ [[package]] name = "objc-sys" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99e1d07c6eab1ce8b6382b8e3c7246fe117ff3f8b34be065f5ebace6749fe845" +checksum = "c7c71324e4180d0899963fc83d9d241ac39e699609fc1025a850aadac8257459" [[package]] name = "objc2" @@ -4004,12 +4034,12 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" dependencies = [ - "atomic-polyfill", "critical-section", + "portable-atomic", ] [[package]] @@ -4158,9 +4188,9 @@ dependencies = [ [[package]] name = "pdf" -version = "0.8.1" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e375ec076445f61d4dbc4636e9e788f841d279c65d6fea8a3875caddd4f2dd82" +checksum = "3afc7e745846405d572daba57a429f30a198d955602aff8a1a9e437c2abfcaa2" dependencies = [ "aes", "bitflags 1.3.2", @@ -4169,10 +4199,11 @@ dependencies = [ "deflate", "fax", "globalcache", - "inflate", + "indexmap 2.1.0", "istring", "itertools 0.10.5", "jpeg-decoder", + "libflate", "log", "md5", "once_cell", @@ -4185,9 +4216,9 @@ dependencies = [ [[package]] name = "pdf_derive" -version = "0.1.22" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f4007262775d0798de87b15cbc64cf1aed5f7ee87eec847e297b69d8ed4b4f8" +checksum = "1038b9cb38dec35eeee9f23eacfb2480087982f9b7e9221efa8034eea9ca2360" dependencies = [ "proc-macro2", "quote", @@ -4305,7 +4336,7 @@ dependencies = [ "cfg-if", "concurrent-queue", "pin-project-lite", - "rustix 0.38.26", + "rustix 0.38.28", "tracing", "windows-sys 0.52.0", ] @@ -4318,9 +4349,9 @@ checksum = "22686f4785f02a4fcc856d3b3bb19bf6c8160d103f7a99cc258bddd0251dc7f2" [[package]] name = "portable-atomic" -version = "1.5.1" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bccab0e7fd7cc19f820a1c8c91720af652d0c88dc9664dd72aef2614f04af3b" +checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0" dependencies = [ "critical-section", ] @@ -4650,9 +4681,9 @@ dependencies = [ [[package]] name = "ring" -version = "0.17.6" +version = "0.17.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "684d5e6e18f669ccebf64a92236bb7db9a34f07be010e3627368182027180866" +checksum = "688c63d65483050968b2a8937f7995f443e27041a0f7700aa59b0822aedebb74" dependencies = [ "cc", "getrandom", @@ -4663,13 +4694,19 @@ dependencies = [ ] [[package]] -name = "rowan" -version = "0.15.13" +name = "rle-decode-fast" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "906057e449592587bf6724f00155bf82a6752c868d78a8fb3aa41f4e6357cfe8" +checksum = "3582f63211428f83597b51b2ddb88e2a91a9d52d12831f9d08f5e624e8977422" + +[[package]] +name = "rowan" +version = "0.15.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a58fa8a7ccff2aec4f39cc45bf5f985cec7125ab271cf681c279fd00192b49" dependencies = [ "countme", - "hashbrown 0.12.3", + "hashbrown 0.14.3", "memoffset 0.9.0", "rustc-hash", "text-size", @@ -4698,9 +4735,9 @@ dependencies = [ [[package]] name = "rust-embed" -version = "8.0.0" +version = "8.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1e7d90385b59f0a6bf3d3b757f3ca4ece2048265d70db20a2016043d4509a40" +checksum = "810294a8a4a0853d4118e3b94bb079905f2107c7fe979d8f0faae98765eb6378" dependencies = [ "rust-embed-impl", "rust-embed-utils", @@ -4709,9 +4746,9 @@ dependencies = [ [[package]] name = "rust-embed-impl" -version = "8.0.0" +version = "8.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c3d8c6fd84090ae348e63a84336b112b5c3918b3bf0493a581f7bd8ee623c29" +checksum = "bfc144a1273124a67b8c1d7cd19f5695d1878b31569c0512f6086f0f4676604e" dependencies = [ "proc-macro2", "quote", @@ -4722,9 +4759,9 @@ dependencies = [ [[package]] name = "rust-embed-utils" -version = "8.0.0" +version = "8.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "873feff8cb7bf86fdf0a71bb21c95159f4e4a37dd7a4bd1855a940909b583ada" +checksum = "816ccd4875431253d6bb54b804bcff4369cbde9bae33defde25fdf6c2ef91d40" dependencies = [ "sha2", "walkdir", @@ -4808,9 +4845,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.26" +version = "0.38.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9470c4bf8246c8daf25f9598dca807fb6510347b1e1cfa55749113850c79d88a" +checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316" dependencies = [ "bitflags 2.4.1", "errno", @@ -4821,9 +4858,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.21.9" +version = "0.21.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "629648aced5775d558af50b2b4c7b02983a04b312126d45eeead26e7caa498b9" +checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba" dependencies = [ "log", "ring", @@ -4891,9 +4928,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.15" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" +checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" [[package]] name = "same-file" @@ -5211,7 +5248,7 @@ dependencies = [ "libc", "log", "memmap2 0.9.0", - "rustix 0.38.26", + "rustix 0.38.28", "thiserror", "wayland-backend", "wayland-client", @@ -5295,7 +5332,7 @@ dependencies = [ "objc", "raw-window-handle 0.5.2", "redox_syscall 0.4.1", - "rustix 0.38.26", + "rustix 0.38.28", "tiny-xlib", "wasm-bindgen", "wayland-backend", @@ -5659,7 +5696,7 @@ dependencies = [ "cfg-if", "fastrand 2.0.1", "redox_syscall 0.4.1", - "rustix 0.38.26", + "rustix 0.38.28", "windows-sys 0.48.0", ] @@ -5752,9 +5789,9 @@ dependencies = [ [[package]] name = "tiny-skia" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b72a92a05db376db09fe6d50b7948d106011761c05a6a45e23e17ee9b556222" +checksum = "b6a067b809476893fce6a254cf285850ff69c847e6cfbade6a20b655b6c7e80d" dependencies = [ "arrayref", "arrayvec", @@ -5767,9 +5804,9 @@ dependencies = [ [[package]] name = "tiny-skia-path" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ac3865b9708fc7e1961a65c3a4fa55e984272f33092d3c859929f887fceb647" +checksum = "5de35e8a90052baaaf61f171680ac2f8e925a1e43ea9d2e3a00514772250e541" dependencies = [ "arrayref", "bytemuck", @@ -6039,18 +6076,18 @@ dependencies = [ [[package]] name = "unic-langid" -version = "0.9.1" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "398f9ad7239db44fd0f80fe068d12ff22d78354080332a5077dc6f52f14dcf2f" +checksum = "887622f8e7b723780c5e64b04dcc0c9b8f426ada7cca6790cd3ea3bf0f08037a" dependencies = [ "unic-langid-impl", ] [[package]] name = "unic-langid-impl" -version = "0.9.1" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e35bfd2f2b8796545b55d7d3fd3e89a0613f68a0d1c8bc28cb7ff96b411a35ff" +checksum = "5adeb847e35eed4efbffd9fb2e4d078b91ece56e4d6a3c0d2df55b3a1dac07d5" dependencies = [ "serde", "tinystr", @@ -6067,9 +6104,9 @@ dependencies = [ [[package]] name = "unicode-bidi" -version = "0.3.13" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" +checksum = "6f2528f27a9eb2b21e69c95319b30bd0efd85d09c379741b0f78ea1d86be2416" [[package]] name = "unicode-bidi-mirroring" @@ -6581,7 +6618,7 @@ dependencies = [ "either", "home", "once_cell", - "rustix 0.38.26", + "rustix 0.38.28", ] [[package]] @@ -6878,7 +6915,7 @@ dependencies = [ "percent-encoding", "raw-window-handle 0.5.2", "redox_syscall 0.3.5", - "rustix 0.38.26", + "rustix 0.38.28", "sctk-adwaita", "smithay-client-toolkit", "smol_str", @@ -6899,9 +6936,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.5.19" +version = "0.5.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "829846f3e3db426d4cee4510841b71a8e58aa2a76b1132579487ae430ccd9c7b" +checksum = "b67b5f0a4e7a27a64c651977932b9dc5667ca7fc31ac44b03ed37a0cf42fdfff" dependencies = [ "memchr", ] @@ -6963,9 +7000,9 @@ dependencies = [ [[package]] name = "xattr" -version = "1.0.1" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4686009f71ff3e5c4dbcf1a282d0a44db3f021ba69350cd42086b3e5f1c6985" +checksum = "fbc6ab6ec1907d1a901cdbcd2bd4cb9e7d64ce5c9739cbb97d3c391acd8c7fae" dependencies = [ "libc", ] @@ -7120,18 +7157,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.7.28" +version = "0.7.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d6f15f7ade05d2a4935e34a457b936c23dc70a05cc1d97133dc99e7a3fe0f0e" +checksum = "306dca4455518f1f31635ec308b6b3e4eb1b11758cefafc782827d0aa7acb5c7" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.28" +version = "0.7.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbbad221e3f78500350ecbd7dfa4e63ef945c05f4c61cb7f4d3f84cd0bba649b" +checksum = "be912bf68235a88fbefd1b73415cb218405958d1655b2ece9035a19920bdf6ba" dependencies = [ "proc-macro2", "quote", diff --git a/ci_tester/src/main.rs b/ci_tester/src/main.rs index 44a3c11..a41a410 100644 --- a/ci_tester/src/main.rs +++ b/ci_tester/src/main.rs @@ -316,17 +316,18 @@ fn collect_all_files_and_dirs(dir: &str) -> std::io::Result { let rd = fs::read_dir(folder)?; for entry in rd { let entry = entry?; - let path = entry.path(); + let file_type = entry.file_type()?; + let path_str = entry.path().to_string_lossy().to_string(); - if path.is_dir() { - folders.insert(path.to_string_lossy().to_string()); - folders_to_check.push(path.to_string_lossy().to_string()); - } else if path.is_symlink() { - symlinks.insert(path.to_string_lossy().to_string()); - } else if path.is_file() { - files.insert(path.to_string_lossy().to_string()); + if file_type.is_dir() { + folders.insert(path_str.clone()); + folders_to_check.push(path_str); + } else if file_type.is_symlink() { + symlinks.insert(path_str); + } else if file_type.is_file() { + files.insert(path_str); } else { - panic!("Unknown type of file {:?}", path); + panic!("Unknown type of file {path_str:?}"); } } } diff --git a/czkawka_cli/Cargo.toml b/czkawka_cli/Cargo.toml index 1a3d539..d3838f6 100644 --- a/czkawka_cli/Cargo.toml +++ b/czkawka_cli/Cargo.toml @@ -10,14 +10,14 @@ homepage = "https://github.com/qarmin/czkawka" repository = "https://github.com/qarmin/czkawka" [dependencies] -clap = { version = "4.3", features = ["derive"] } +clap = { version = "4.4", features = ["derive"] } # For enum types image_hasher = "1.2" log = "0.4.20" handsome_logger = "0.8" -fun_time = { version = "0.3.1", features = ["log"] } +fun_time = { version = "0.3", features = ["log"] } czkawka_core = { path = "../czkawka_core", version = "6.1.0", features = [] } [features] diff --git a/czkawka_core/Cargo.toml b/czkawka_core/Cargo.toml index 897d4dd..625c1c7 100644 --- a/czkawka_core/Cargo.toml +++ b/czkawka_core/Cargo.toml @@ -8,7 +8,7 @@ description = "Core of Czkawka app" license = "MIT" homepage = "https://github.com/qarmin/czkawka" repository = "https://github.com/qarmin/czkawka" - +build = "build.rs" [dependencies] humansize = "2.1" @@ -26,12 +26,12 @@ hamming = "0.1" # Needed by same music bitflags = "2.4" -lofty = "0.16" +lofty = "0.17" # Needed by broken files zip = { version = "0.6", features = ["aes-crypto", "bzip2", "deflate", "time"], default-features = false } audio_checker = "0.1" -pdf = "0.8" +pdf = "0.9" # Needed by audio similarity feature rusty-chromaprint = "0.1" @@ -56,8 +56,8 @@ serde_json = "1.0" # Language i18n-embed = { version = "0.14", features = ["fluent-system", "desktop-requester"] } i18n-embed-fl = "0.7" -rust-embed = { version = "8.0", features = ["debug-embed"] } -once_cell = "1.18" +rust-embed = { version = "8.1", features = ["debug-embed"] } +once_cell = "1.19" # Raw image files rawloader = "0.37" @@ -77,12 +77,14 @@ anyhow = { version = "1.0" } state = "0.6" os_info = { version = "3", default-features = false } -rustc_version = "0.4" log = "0.4.20" handsome_logger = "0.8" -fun_time = { version = "0.3.1", features = ["log"] } +fun_time = { version = "0.3", features = ["log"] } + +[build-dependencies] +rustc_version = "0.4" [features] default = [] heif = ["dep:libheif-rs", "dep:libheif-sys"] -libraw = ["dep:libraw-rs"] \ No newline at end of file +libraw = ["dep:libraw-rs"] diff --git a/czkawka_core/build.rs b/czkawka_core/build.rs new file mode 100644 index 0000000..15a3fe7 --- /dev/null +++ b/czkawka_core/build.rs @@ -0,0 +1,18 @@ +fn main() { + let rust_version = match rustc_version::version_meta() { + Ok(meta) => { + let rust_v = meta.semver.to_string(); + let rust_date = meta.commit_date.unwrap_or_default(); + format!("{rust_v} ({rust_date})") + } + Err(_) => "".to_string(), + }; + println!("cargo:rustc-env=RUST_VERSION_INTERNAL={rust_version}"); + + // Find if app is build with cranelift + if let Ok(codegen) = std::env::var("CARGO_PROFILE_RELEASE_CODEGEN_UNITS") { + if codegen == "1" { + println!("cargo:rustc-env=USING_CRANELIFT=1"); + } + } +} diff --git a/czkawka_core/src/bad_extensions.rs b/czkawka_core/src/bad_extensions.rs index 5d8bc11..04c6c5b 100644 --- a/czkawka_core/src/bad_extensions.rs +++ b/czkawka_core/src/bad_extensions.rs @@ -231,9 +231,7 @@ impl BadExtensions { true } - DirTraversalResult::SuccessFolders { .. } => { - unreachable!() - } + DirTraversalResult::Stopped => false, } } diff --git a/czkawka_core/src/big_file.rs b/czkawka_core/src/big_file.rs index 6ccf27a..3b471d8 100644 --- a/czkawka_core/src/big_file.rs +++ b/czkawka_core/src/big_file.rs @@ -2,7 +2,7 @@ use std::collections::BTreeMap; use std::fs; use std::fs::DirEntry; use std::io::Write; -use std::path::{Path, PathBuf}; +use std::path::PathBuf; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Arc; @@ -106,14 +106,13 @@ impl BigFile { check_folder_children( &mut dir_result, &mut warnings, - ¤t_folder, &entry_data, self.common_data.recursive_search, &self.common_data.directories, &self.common_data.excluded_items, ); } else if file_type.is_file() { - self.collect_file_entry(&atomic_counter, &entry_data, &mut fe_result, &mut warnings, ¤t_folder); + self.collect_file_entry(&atomic_counter, &entry_data, &mut fe_result, &mut warnings); } } (dir_result, warnings, fe_result) @@ -142,20 +141,13 @@ impl BigFile { true } - pub fn collect_file_entry( - &self, - atomic_counter: &Arc, - entry_data: &DirEntry, - fe_result: &mut Vec<(u64, FileEntry)>, - warnings: &mut Vec, - current_folder: &Path, - ) { + pub fn collect_file_entry(&self, atomic_counter: &Arc, entry_data: &DirEntry, fe_result: &mut Vec<(u64, FileEntry)>, warnings: &mut Vec) { atomic_counter.fetch_add(1, Ordering::Relaxed); if !self.common_data.allowed_extensions.check_if_entry_ends_with_extension(entry_data) { return; } - let current_file_name = current_folder.join(entry_data.file_name()); + let current_file_name = entry_data.path(); if self.common_data.excluded_items.is_excluded(¤t_file_name) { return; } diff --git a/czkawka_core/src/broken_files.rs b/czkawka_core/src/broken_files.rs index 8ad76af..bc5fb41 100644 --- a/czkawka_core/src/broken_files.rs +++ b/czkawka_core/src/broken_files.rs @@ -144,14 +144,13 @@ impl BrokenFiles { check_folder_children( &mut dir_result, &mut warnings, - ¤t_folder, &entry_data, self.common_data.recursive_search, &self.common_data.directories, &self.common_data.excluded_items, ); } else if file_type.is_file() { - if let Some(file_entry) = self.get_file_entry(&atomic_counter, &entry_data, &mut warnings, ¤t_folder) { + if let Some(file_entry) = self.get_file_entry(&atomic_counter, &entry_data, &mut warnings) { fe_result.push((file_entry.path.to_string_lossy().to_string(), file_entry)); } } @@ -178,7 +177,7 @@ impl BrokenFiles { true } - fn get_file_entry(&self, atomic_counter: &Arc, entry_data: &DirEntry, warnings: &mut Vec, current_folder: &Path) -> Option { + fn get_file_entry(&self, atomic_counter: &Arc, entry_data: &DirEntry, warnings: &mut Vec) -> Option { atomic_counter.fetch_add(1, Ordering::Relaxed); if !self.common_data.allowed_extensions.check_if_entry_ends_with_extension(entry_data) { return None; @@ -191,7 +190,7 @@ impl BrokenFiles { return None; } - let current_file_name = current_folder.join(entry_data.file_name()); + let current_file_name = entry_data.path(); if self.common_data.excluded_items.is_excluded(¤t_file_name) { return None; } diff --git a/czkawka_core/src/common.rs b/czkawka_core/src/common.rs index bf45b94..76958f5 100644 --- a/czkawka_core/src/common.rs +++ b/czkawka_core/src/common.rs @@ -75,10 +75,7 @@ pub fn get_available_threads() -> usize { } pub fn print_version_mode() { - let rust_version = match rustc_version::version_meta() { - Ok(meta) => meta.semver.to_string(), - Err(_) => "".to_string(), - }; + let rust_version = env!("RUST_VERSION_INTERNAL"); let debug_release = if cfg!(debug_assertions) { "debug" } else { "release" }; let processors = get_available_threads(); @@ -94,6 +91,10 @@ pub fn print_version_mode() { if cfg!(debug_assertions) { warn!("You are running debug version of app which is a lot of slower than release version."); } + + if option_env!("USING_CRANELIFT").is_some() { + warn!("You are running app with cranelift which is intended only for fast compilation, not runtime performance."); + } } pub fn set_default_number_of_threads() { @@ -173,7 +174,11 @@ pub fn remove_folder_if_contains_only_empty_folders(path: impl AsRef) -> b let Some(entry) = entries_to_check.pop() else { break; }; - if !entry.path().is_dir() { + let Some(file_type) = entry.file_type().ok() else { + return false; + }; + + if !file_type.is_dir() { return false; } let Ok(internal_read_dir) = entry.path().read_dir() else { @@ -421,7 +426,6 @@ pub fn normalize_windows_path(path_to_change: impl AsRef) -> PathBuf { pub fn check_folder_children( dir_result: &mut Vec, warnings: &mut Vec, - current_folder: &Path, entry_data: &DirEntry, recursive_search: bool, directories: &Directories, @@ -431,25 +435,25 @@ pub fn check_folder_children( return; } - let next_folder = current_folder.join(entry_data.file_name()); - if directories.is_excluded(&next_folder) { + let next_item = entry_data.path(); + if directories.is_excluded(&next_item) { return; } - if excluded_items.is_excluded(&next_folder) { + if excluded_items.is_excluded(&next_item) { return; } #[cfg(target_family = "unix")] if directories.exclude_other_filesystems() { - match directories.is_on_other_filesystems(&next_folder) { + match directories.is_on_other_filesystems(&next_item) { Ok(true) => return, Err(e) => warnings.push(e), _ => (), } } - dir_result.push(next_folder); + dir_result.push(next_item); } // Here we assume, that internal Vec<> have at least 1 object diff --git a/czkawka_core/src/common_dir_traversal.rs b/czkawka_core/src/common_dir_traversal.rs index 71d6046..63df001 100644 --- a/czkawka_core/src/common_dir_traversal.rs +++ b/czkawka_core/src/common_dir_traversal.rs @@ -1,4 +1,4 @@ -use std::collections::{BTreeMap, HashMap}; +use std::collections::BTreeMap; use std::fs; use std::fs::{DirEntry, FileType, Metadata, ReadDir}; use std::path::{Path, PathBuf}; @@ -95,31 +95,8 @@ pub enum ErrorType { NonExistentFile, } -// Empty folders - -/// Enum with values which show if folder is empty. -/// In function "`optimize_folders`" automatically "Maybe" is changed to "Yes", so it is not necessary to put it here -#[derive(Eq, PartialEq, Copy, Clone, Debug)] -pub(crate) enum FolderEmptiness { - No, - Maybe, -} - -/// Struct assigned to each checked folder with parent path(used to ignore parent if children are not empty) and flag which shows if folder is empty -#[derive(Clone, Debug)] -pub struct FolderEntry { - pub path: PathBuf, - pub(crate) parent_path: Option, - // Usable only when finding - pub(crate) is_empty: FolderEmptiness, - pub modified_date: u64, -} - -// Collection mode (files / empty folders) - #[derive(Copy, Clone, Eq, PartialEq)] pub enum Collect { - EmptyFolders, InvalidSymlinks, Files, } @@ -315,10 +292,6 @@ pub enum DirTraversalResult { warnings: Vec, grouped_file_entries: BTreeMap>, }, - SuccessFolders { - warnings: Vec, - folder_entries: HashMap, // Path, FolderEntry - }, Stopped, } @@ -345,22 +318,7 @@ where let mut all_warnings = vec![]; let mut grouped_file_entries: BTreeMap> = BTreeMap::new(); - let mut folder_entries: HashMap = HashMap::new(); - // Add root folders into result (only for empty folder collection) - if self.collect == Collect::EmptyFolders { - for dir in &self.root_dirs { - folder_entries.insert( - dir.to_string_lossy().to_string(), - FolderEntry { - path: dir.clone(), - parent_path: None, - is_empty: FolderEmptiness::Maybe, - modified_date: 0, - }, - ); - } - } // Add root folders for finding let mut folders_to_check: Vec = self.root_dirs.clone(); @@ -391,19 +349,14 @@ where let mut dir_result = vec![]; let mut warnings = vec![]; let mut fe_result = vec![]; - let mut set_as_not_empty_folder_list = vec![]; - let mut folder_entries_list = vec![]; let Some(read_dir) = common_read_dir(¤t_folder, &mut warnings) else { - if collect == Collect::EmptyFolders { - set_as_not_empty_folder_list.push(current_folder); - } - return (dir_result, warnings, fe_result, set_as_not_empty_folder_list, folder_entries_list); + return (dir_result, warnings, fe_result); }; let mut counter = 0; // Check every sub folder/file/link etc. - 'dir: for entry in read_dir { + for entry in read_dir { let Some(entry_data) = common_get_entry_data(&entry, &mut warnings, ¤t_folder) else { continue; }; @@ -411,20 +364,7 @@ where match (entry_type(file_type), collect) { (EntryType::Dir, Collect::Files | Collect::InvalidSymlinks) => { - process_dir_in_file_symlink_mode(recursive_search, ¤t_folder, entry_data, &directories, &mut dir_result, &mut warnings, &excluded_items); - } - (EntryType::Dir, Collect::EmptyFolders) => { - counter += 1; - process_dir_in_dir_mode( - ¤t_folder, - entry_data, - &directories, - &mut dir_result, - &mut warnings, - &excluded_items, - &mut set_as_not_empty_folder_list, - &mut folder_entries_list, - ); + process_dir_in_file_symlink_mode(recursive_search, entry_data, &directories, &mut dir_result, &mut warnings, &excluded_items); } (EntryType::File, Collect::Files) => { counter += 1; @@ -433,39 +373,18 @@ where &mut warnings, &mut fe_result, &allowed_extensions, - ¤t_folder, &directories, &excluded_items, minimal_file_size, maximal_file_size, ); } - (EntryType::File | EntryType::Symlink, Collect::EmptyFolders) => { - #[cfg(target_family = "unix")] - if directories.exclude_other_filesystems() { - match directories.is_on_other_filesystems(¤t_folder) { - Ok(true) => continue 'dir, - Err(e) => warnings.push(e.to_string()), - _ => (), - } - } - - set_as_not_empty_folder_list.push(current_folder.clone()); - } (EntryType::File, Collect::InvalidSymlinks) => { counter += 1; } (EntryType::Symlink, Collect::InvalidSymlinks) => { counter += 1; - process_symlink_in_symlink_mode( - entry_data, - &mut warnings, - &mut fe_result, - &allowed_extensions, - ¤t_folder, - &directories, - &excluded_items, - ); + process_symlink_in_symlink_mode(entry_data, &mut warnings, &mut fe_result, &allowed_extensions, &directories, &excluded_items); } (EntryType::Symlink, Collect::Files) | (EntryType::Other, _) => { // nothing to do @@ -476,47 +395,33 @@ where // Increase counter in batch, because usually it may be slow to add multiple times atomic value atomic_counter.fetch_add(counter, Ordering::Relaxed); } - (dir_result, warnings, fe_result, set_as_not_empty_folder_list, folder_entries_list) + (dir_result, warnings, fe_result) }) .collect(); - let required_size = segments.iter().map(|(segment, _, _, _, _)| segment.len()).sum::(); + let required_size = segments.iter().map(|(segment, _, _)| segment.len()).sum::(); folders_to_check = Vec::with_capacity(required_size); // Process collected data - for (segment, warnings, fe_result, set_as_not_empty_folder_list, fe_list) in segments { + for (segment, warnings, fe_result) in segments { folders_to_check.extend(segment); all_warnings.extend(warnings); for fe in fe_result { let key = (self.group_by)(&fe); grouped_file_entries.entry(key).or_default().push(fe); } - for current_folder in &set_as_not_empty_folder_list { - set_as_not_empty_folder(&mut folder_entries, current_folder); - } - for (path, entry) in fe_list { - folder_entries.insert(path.to_string_lossy().to_string(), entry); - } } } send_info_and_wait_for_ending_all_threads(&progress_thread_run, progress_thread_handle); - debug!( - "Collected {} files, {} folders", - grouped_file_entries.values().map(Vec::len).sum::(), - folder_entries.len() - ); + debug!("Collected {} files", grouped_file_entries.values().map(Vec::len).sum::()); match collect { Collect::Files | Collect::InvalidSymlinks => DirTraversalResult::SuccessFiles { grouped_file_entries, warnings: all_warnings, }, - Collect::EmptyFolders => DirTraversalResult::SuccessFolders { - folder_entries, - warnings: all_warnings, - }, } } } @@ -526,7 +431,6 @@ fn process_file_in_file_mode( warnings: &mut Vec, fe_result: &mut Vec, allowed_extensions: &Extensions, - current_folder: &Path, directories: &Directories, excluded_items: &ExcludedItems, minimal_file_size: u64, @@ -536,7 +440,7 @@ fn process_file_in_file_mode( return; } - let current_file_name = current_folder.join(entry_data.file_name()); + let current_file_name = entry_data.path(); if excluded_items.is_excluded(¤t_file_name) { return; } @@ -550,7 +454,7 @@ fn process_file_in_file_mode( } } - let Some(metadata) = common_get_metadata_dir(entry_data, warnings, current_folder) else { + let Some(metadata) = common_get_metadata_dir(entry_data, warnings, ¤t_file_name) else { return; }; @@ -568,51 +472,8 @@ fn process_file_in_file_mode( } } -fn process_dir_in_dir_mode( - current_folder: &Path, - entry_data: &DirEntry, - directories: &Directories, - dir_result: &mut Vec, - warnings: &mut Vec, - excluded_items: &ExcludedItems, - set_as_not_empty_folder_list: &mut Vec, - folder_entries_list: &mut Vec<(PathBuf, FolderEntry)>, -) { - let next_folder = current_folder.join(entry_data.file_name()); - if excluded_items.is_excluded(&next_folder) || directories.is_excluded(&next_folder) { - set_as_not_empty_folder_list.push(current_folder.to_path_buf()); - return; - } - - #[cfg(target_family = "unix")] - if directories.exclude_other_filesystems() { - match directories.is_on_other_filesystems(&next_folder) { - Ok(true) => return, - Err(e) => warnings.push(e), - _ => (), - } - } - - let Some(metadata) = common_get_metadata_dir(entry_data, warnings, current_folder) else { - set_as_not_empty_folder_list.push(current_folder.to_path_buf()); - return; - }; - - dir_result.push(next_folder.clone()); - folder_entries_list.push(( - next_folder.clone(), - FolderEntry { - path: next_folder, - parent_path: Some(current_folder.to_string_lossy().to_string()), - is_empty: FolderEmptiness::Maybe, - modified_date: get_modified_time(&metadata, warnings, current_folder, true), - }, - )); -} - fn process_dir_in_file_symlink_mode( recursive_search: bool, - current_folder: &Path, entry_data: &DirEntry, directories: &Directories, dir_result: &mut Vec, @@ -623,25 +484,25 @@ fn process_dir_in_file_symlink_mode( return; } - let next_folder = current_folder.join(entry_data.file_name()); - if directories.is_excluded(&next_folder) { + let dir_path = entry_data.path(); + if directories.is_excluded(&dir_path) { return; } - if excluded_items.is_excluded(&next_folder) { + if excluded_items.is_excluded(&dir_path) { return; } #[cfg(target_family = "unix")] if directories.exclude_other_filesystems() { - match directories.is_on_other_filesystems(&next_folder) { + match directories.is_on_other_filesystems(&dir_path) { Ok(true) => return, Err(e) => warnings.push(e), _ => (), } } - dir_result.push(next_folder); + dir_result.push(dir_path); } fn process_symlink_in_symlink_mode( @@ -649,7 +510,6 @@ fn process_symlink_in_symlink_mode( warnings: &mut Vec, fe_result: &mut Vec, allowed_extensions: &Extensions, - current_folder: &Path, directories: &Directories, excluded_items: &ExcludedItems, ) { @@ -657,21 +517,21 @@ fn process_symlink_in_symlink_mode( return; } - let current_file_name = current_folder.join(entry_data.file_name()); + let current_file_name = entry_data.path(); if excluded_items.is_excluded(¤t_file_name) { return; } #[cfg(target_family = "unix")] if directories.exclude_other_filesystems() { - match directories.is_on_other_filesystems(current_folder) { + match directories.is_on_other_filesystems(¤t_file_name) { Ok(true) => return, Err(e) => warnings.push(e), _ => (), } } - let Some(metadata) = common_get_metadata_dir(entry_data, warnings, current_folder) else { + let Some(metadata) = common_get_metadata_dir(entry_data, warnings, ¤t_file_name) else { return; }; @@ -826,20 +686,3 @@ pub fn get_lowercase_name(entry_data: &DirEntry, warnings: &mut Vec) -> .to_lowercase(); Some(name) } - -fn set_as_not_empty_folder(folder_entries: &mut HashMap, current_folder: &Path) { - let mut d = folder_entries.get_mut(current_folder.to_string_lossy().as_ref()).unwrap(); - // Loop to recursively set as non empty this and all his parent folders - loop { - d.is_empty = FolderEmptiness::No; - if d.parent_path.is_some() { - let cf = d.parent_path.clone().unwrap(); - d = folder_entries.get_mut(&cf).unwrap(); - if d.is_empty == FolderEmptiness::No { - break; // Already set as non empty, so one of child already set it to non empty - } - } else { - break; - } - } -} diff --git a/czkawka_core/src/duplicate.rs b/czkawka_core/src/duplicate.rs index 048b3ac..96a3e7e 100644 --- a/czkawka_core/src/duplicate.rs +++ b/czkawka_core/src/duplicate.rs @@ -204,9 +204,6 @@ impl DuplicateFinder { true } - DirTraversalResult::SuccessFolders { .. } => { - unreachable!() - } DirTraversalResult::Stopped => false, } } @@ -280,9 +277,7 @@ impl DuplicateFinder { true } - DirTraversalResult::SuccessFolders { .. } => { - unreachable!() - } + DirTraversalResult::Stopped => false, } } @@ -356,9 +351,7 @@ impl DuplicateFinder { true } - DirTraversalResult::SuccessFolders { .. } => { - unreachable!() - } + DirTraversalResult::Stopped => false, } } diff --git a/czkawka_core/src/empty_files.rs b/czkawka_core/src/empty_files.rs index fc9dd44..ce2647d 100644 --- a/czkawka_core/src/empty_files.rs +++ b/czkawka_core/src/empty_files.rs @@ -77,9 +77,7 @@ impl EmptyFiles { true } - DirTraversalResult::SuccessFolders { .. } => { - unreachable!() - } + DirTraversalResult::Stopped => false, } } diff --git a/czkawka_core/src/empty_folder.rs b/czkawka_core/src/empty_folder.rs index 1ac9210..16e6f09 100644 --- a/czkawka_core/src/empty_folder.rs +++ b/czkawka_core/src/empty_folder.rs @@ -1,22 +1,45 @@ use std::collections::HashMap; use std::fs; +use std::fs::DirEntry; use std::io::Write; +use std::path::{Path, PathBuf}; +use std::sync::atomic::Ordering; +use crate::common::{check_if_stop_received, prepare_thread_handler_common, send_info_and_wait_for_ending_all_threads}; use crossbeam_channel::{Receiver, Sender}; use fun_time::fun_time; use log::debug; use rayon::prelude::*; -use crate::common_dir_traversal::{Collect, DirTraversalBuilder, DirTraversalResult, FolderEmptiness, FolderEntry, ProgressData, ToolType}; +use crate::common_dir_traversal::{common_get_entry_data, common_get_metadata_dir, common_read_dir, get_modified_time, CheckingMethod, ProgressData, ToolType}; +use crate::common_directory::Directories; +use crate::common_items::ExcludedItems; use crate::common_tool::{CommonData, CommonToolData, DeleteMethod}; use crate::common_traits::{DebugPrint, PrintResults}; +#[derive(Clone, Debug)] +pub struct FolderEntry { + pub path: PathBuf, + pub(crate) parent_path: Option, + // Usable only when finding + pub(crate) is_empty: FolderEmptiness, + pub modified_date: u64, +} + pub struct EmptyFolder { common_data: CommonToolData, information: Info, empty_folder_list: HashMap, // Path, FolderEntry } +/// Enum with values which show if folder is empty. +/// In function "`optimize_folders`" automatically "Maybe" is changed to "Yes", so it is not necessary to put it here +#[derive(Eq, PartialEq, Copy, Clone, Debug)] +pub(crate) enum FolderEmptiness { + No, + Maybe, +} + #[derive(Default)] pub struct Info { pub number_of_empty_folders: usize, @@ -73,36 +96,172 @@ impl EmptyFolder { #[fun_time(message = "check_for_empty_folders", level = "debug")] fn check_for_empty_folders(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&Sender>) -> bool { - let result = DirTraversalBuilder::new() - .root_dirs(self.common_data.directories.included_directories.clone()) - .group_by(|_fe| ()) - .stop_receiver(stop_receiver) - .progress_sender(progress_sender) - .directories(self.common_data.directories.clone()) - .excluded_items(self.common_data.excluded_items.clone()) - .collect(Collect::EmptyFolders) - .max_stage(0) - .tool_type(self.common_data.tool_type) - .build() - .run(); + let mut folders_to_check: Vec = self.common_data.directories.included_directories.clone(); - match result { - DirTraversalResult::SuccessFiles { .. } => { - unreachable!() - } - DirTraversalResult::SuccessFolders { folder_entries, warnings } => { - for (name, folder_entry) in folder_entries { - if folder_entry.is_empty != FolderEmptiness::No { - self.empty_folder_list.insert(name, folder_entry); - } - } + let (progress_thread_handle, progress_thread_run, atomic_counter, _check_was_stopped) = + prepare_thread_handler_common(progress_sender, 0, 0, 0, CheckingMethod::None, self.common_data.tool_type); - self.common_data.text_messages.warnings.extend(warnings); - debug!("Found {} empty folders.", self.empty_folder_list.len()); - true - } - DirTraversalResult::Stopped => false, + let excluded_items = self.common_data.excluded_items.clone(); + let directories = self.common_data.directories.clone(); + + let mut folder_entries: HashMap = HashMap::new(); + let mut non_empty_folders: Vec = vec![]; + + for dir in &folders_to_check { + folder_entries.insert( + dir.to_string_lossy().to_string(), + FolderEntry { + path: dir.clone(), + parent_path: None, + is_empty: FolderEmptiness::Maybe, + modified_date: 0, + }, + ); } + + while !folders_to_check.is_empty() { + if check_if_stop_received(stop_receiver) { + send_info_and_wait_for_ending_all_threads(&progress_thread_run, progress_thread_handle); + return false; + } + + let segments: Vec<_> = folders_to_check + .into_par_iter() + .map(|current_folder| { + let mut dir_result = vec![]; + let mut warnings = vec![]; + let mut set_as_not_empty_folder_list = vec![]; + let mut folder_entries_list = vec![]; + + let Some(read_dir) = common_read_dir(¤t_folder, &mut warnings) else { + set_as_not_empty_folder_list.push(current_folder.to_string_lossy().to_string()); + return (dir_result, warnings, set_as_not_empty_folder_list, folder_entries_list); + }; + + let mut counter = 0; + // Check every sub folder/file/link etc. + for entry in read_dir { + let Some(entry_data) = common_get_entry_data(&entry, &mut warnings, ¤t_folder) else { + continue; + }; + let Ok(file_type) = entry_data.file_type() else { continue }; + + if file_type.is_dir() { + counter += 1; + Self::process_dir_in_dir_mode( + ¤t_folder, + entry_data, + &directories, + &mut dir_result, + &mut warnings, + &excluded_items, + &mut set_as_not_empty_folder_list, + &mut folder_entries_list, + ); + } else { + set_as_not_empty_folder_list.push(current_folder.to_string_lossy().to_string()); + } + } + if counter > 0 { + // Increase counter in batch, because usually it may be slow to add multiple times atomic value + atomic_counter.fetch_add(counter, Ordering::Relaxed); + } + (dir_result, warnings, set_as_not_empty_folder_list, folder_entries_list) + }) + .collect(); + + let required_size = segments.iter().map(|(segment, _, _, _)| segment.len()).sum::(); + folders_to_check = Vec::with_capacity(required_size); + + // Process collected data + for (segment, warnings, set_as_not_empty_folder_list, fe_list) in segments { + folders_to_check.extend(segment); + self.common_data.text_messages.warnings.extend(warnings); + non_empty_folders.extend(set_as_not_empty_folder_list); + for (path, entry) in fe_list { + folder_entries.insert(path, entry); + } + } + } + + // Start to + for current_folder in non_empty_folders.into_iter().rev() { + Self::set_as_not_empty_folder(&mut folder_entries, ¤t_folder); + } + + for (name, folder_entry) in folder_entries { + if folder_entry.is_empty != FolderEmptiness::No { + self.empty_folder_list.insert(name, folder_entry); + } + } + + debug!("Found {} empty folders.", self.empty_folder_list.len()); + send_info_and_wait_for_ending_all_threads(&progress_thread_run, progress_thread_handle); + true + } + + pub(crate) fn set_as_not_empty_folder(folder_entries: &mut HashMap, current_folder: &str) { + let mut d = folder_entries.get_mut(current_folder).unwrap(); + if d.is_empty == FolderEmptiness::No { + return; // Already set as non empty by one of his child + } + + // Loop to recursively set as non empty this and all his parent folders + loop { + d.is_empty = FolderEmptiness::No; + if d.parent_path.is_some() { + let cf = d.parent_path.clone().unwrap(); + d = folder_entries.get_mut(&cf).unwrap(); + if d.is_empty == FolderEmptiness::No { + break; // Already set as non empty, so one of child already set it to non empty + } + } else { + break; + } + } + } + + fn process_dir_in_dir_mode( + current_folder: &Path, + entry_data: &DirEntry, + directories: &Directories, + dir_result: &mut Vec, + warnings: &mut Vec, + excluded_items: &ExcludedItems, + set_as_not_empty_folder_list: &mut Vec, + folder_entries_list: &mut Vec<(String, FolderEntry)>, + ) { + let parent_folder_str = current_folder.to_string_lossy().to_string(); + let next_folder = entry_data.path(); + if excluded_items.is_excluded(&next_folder) || directories.is_excluded(&next_folder) { + set_as_not_empty_folder_list.push(parent_folder_str); + return; + } + + #[cfg(target_family = "unix")] + if directories.exclude_other_filesystems() { + match directories.is_on_other_filesystems(&next_folder) { + Ok(true) => return, + Err(e) => warnings.push(e), + _ => (), + } + } + + let Some(metadata) = common_get_metadata_dir(entry_data, warnings, &next_folder) else { + set_as_not_empty_folder_list.push(parent_folder_str); + return; + }; + + dir_result.push(next_folder.clone()); + folder_entries_list.push(( + next_folder.to_string_lossy().to_string(), + FolderEntry { + path: next_folder, + parent_path: Some(parent_folder_str), + is_empty: FolderEmptiness::Maybe, + modified_date: get_modified_time(&metadata, warnings, current_folder, true), + }, + )); } #[fun_time(message = "delete_files", level = "debug")] diff --git a/czkawka_core/src/invalid_symlinks.rs b/czkawka_core/src/invalid_symlinks.rs index 764e094..c5f5273 100644 --- a/czkawka_core/src/invalid_symlinks.rs +++ b/czkawka_core/src/invalid_symlinks.rs @@ -64,7 +64,6 @@ impl InvalidSymlinks { debug!("Found {} invalid symlinks.", self.information.number_of_invalid_symlinks); true } - DirTraversalResult::SuccessFolders { .. } => unreachable!(), DirTraversalResult::Stopped => false, } } diff --git a/czkawka_core/src/same_music.rs b/czkawka_core/src/same_music.rs index 4a8b5de..693b7b1 100644 --- a/czkawka_core/src/same_music.rs +++ b/czkawka_core/src/same_music.rs @@ -219,9 +219,7 @@ impl SameMusic { debug!("check_files - Found {} music files.", self.music_to_check.len()); true } - DirTraversalResult::SuccessFolders { .. } => { - unreachable!() - } + DirTraversalResult::Stopped => false, } } diff --git a/czkawka_core/src/similar_images.rs b/czkawka_core/src/similar_images.rs index aed5560..e8921ca 100644 --- a/czkawka_core/src/similar_images.rs +++ b/czkawka_core/src/similar_images.rs @@ -198,7 +198,6 @@ impl SimilarImages { check_folder_children( &mut dir_result, &mut warnings, - ¤t_folder, &entry_data, self.common_data.recursive_search, &self.common_data.directories, @@ -206,7 +205,7 @@ impl SimilarImages { ); } else if file_type.is_file() { atomic_counter.fetch_add(1, Ordering::Relaxed); - self.add_file_entry(¤t_folder, &entry_data, &mut fe_result, &mut warnings); + self.add_file_entry(&entry_data, &mut fe_result, &mut warnings); } } (dir_result, warnings, fe_result) @@ -233,12 +232,12 @@ impl SimilarImages { true } - fn add_file_entry(&self, current_folder: &Path, entry_data: &DirEntry, fe_result: &mut Vec<(String, FileEntry)>, warnings: &mut Vec) { + fn add_file_entry(&self, entry_data: &DirEntry, fe_result: &mut Vec<(String, FileEntry)>, warnings: &mut Vec) { if !self.common_data.allowed_extensions.check_if_entry_ends_with_extension(entry_data) { return; } - let current_file_name = current_folder.join(entry_data.file_name()); + let current_file_name = entry_data.path(); if self.common_data.excluded_items.is_excluded(¤t_file_name) { return; } @@ -249,16 +248,17 @@ impl SimilarImages { // Checking files if (self.common_data.minimal_file_size..=self.common_data.maximal_file_size).contains(&metadata.len()) { + let path_str = current_file_name.to_string_lossy().to_string(); let fe: FileEntry = FileEntry { - path: current_file_name.clone(), size: metadata.len(), dimensions: String::new(), modified_date: get_modified_time(&metadata, warnings, ¤t_file_name, false), + path: current_file_name, hash: Vec::new(), similarity: 0, }; - fe_result.push((current_file_name.to_string_lossy().to_string(), fe)); + fe_result.push((path_str, fe)); } } diff --git a/czkawka_core/src/similar_videos.rs b/czkawka_core/src/similar_videos.rs index e2525b7..1dd1a5f 100644 --- a/czkawka_core/src/similar_videos.rs +++ b/czkawka_core/src/similar_videos.rs @@ -174,7 +174,6 @@ impl SimilarVideos { check_folder_children( &mut dir_result, &mut warnings, - ¤t_folder, &entry_data, self.common_data.recursive_search, &self.common_data.directories, @@ -182,7 +181,7 @@ impl SimilarVideos { ); } else if file_type.is_file() { atomic_counter.fetch_add(1, Ordering::Relaxed); - self.add_video_file_entry(&entry_data, &mut fe_result, &mut warnings, ¤t_folder); + self.add_video_file_entry(&entry_data, &mut fe_result, &mut warnings); } } (dir_result, warnings, fe_result) @@ -207,12 +206,12 @@ impl SimilarVideos { true } - fn add_video_file_entry(&self, entry_data: &DirEntry, fe_result: &mut Vec<(String, FileEntry)>, warnings: &mut Vec, current_folder: &Path) { + fn add_video_file_entry(&self, entry_data: &DirEntry, fe_result: &mut Vec<(String, FileEntry)>, warnings: &mut Vec) { if !self.common_data.allowed_extensions.check_if_entry_ends_with_extension(entry_data) { return; } - let current_file_name = current_folder.join(entry_data.file_name()); + let current_file_name = entry_data.path(); if self.common_data.excluded_items.is_excluded(¤t_file_name) { return; } diff --git a/czkawka_core/src/temporary.rs b/czkawka_core/src/temporary.rs index 22df1df..938665c 100644 --- a/czkawka_core/src/temporary.rs +++ b/czkawka_core/src/temporary.rs @@ -2,7 +2,7 @@ use std::fs; use std::fs::DirEntry; use std::io::prelude::*; -use std::path::{Path, PathBuf}; +use std::path::PathBuf; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Arc; @@ -106,14 +106,13 @@ impl Temporary { check_folder_children( &mut dir_result, &mut warnings, - ¤t_folder, &entry_data, self.common_data.recursive_search, &self.common_data.directories, &self.common_data.excluded_items, ); } else if file_type.is_file() { - if let Some(file_entry) = self.get_file_entry(&atomic_counter, &entry_data, &mut warnings, ¤t_folder) { + if let Some(file_entry) = self.get_file_entry(&atomic_counter, &entry_data, &mut warnings) { fe_result.push(file_entry); } } @@ -140,7 +139,7 @@ impl Temporary { true } - pub fn get_file_entry(&self, atomic_counter: &Arc, entry_data: &DirEntry, warnings: &mut Vec, current_folder: &Path) -> Option { + pub fn get_file_entry(&self, atomic_counter: &Arc, entry_data: &DirEntry, warnings: &mut Vec) -> Option { atomic_counter.fetch_add(1, Ordering::Relaxed); let file_name_lowercase = get_lowercase_name(entry_data, warnings)?; @@ -148,7 +147,7 @@ impl Temporary { if !TEMP_EXTENSIONS.iter().any(|f| file_name_lowercase.ends_with(f)) { return None; } - let current_file_name = current_folder.join(entry_data.file_name()); + let current_file_name = entry_data.path(); if self.common_data.excluded_items.is_excluded(¤t_file_name) { return None; } diff --git a/czkawka_gui/Cargo.toml b/czkawka_gui/Cargo.toml index 743d050..72a7e5a 100644 --- a/czkawka_gui/Cargo.toml +++ b/czkawka_gui/Cargo.toml @@ -29,13 +29,13 @@ open = "5.0" image = "0.24" # To be able to use custom select -regex = "1.9" +regex = "1.10" # To get image_hasher types image_hasher = "1.2" # Move files to trash -trash = "3.0" +trash = "3.1" # For moving files(why std::fs doesn't have such features?) fs_extra = "1.3" @@ -43,12 +43,12 @@ fs_extra = "1.3" # Language i18n-embed = { version = "0.14", features = ["fluent-system", "desktop-requester"] } i18n-embed-fl = "0.7" -rust-embed = { version = "8.0", features = ["debug-embed"] } -once_cell = "1.18" +rust-embed = { version = "8.1", features = ["debug-embed"] } +once_cell = "1.19" log = "0.4.20" handsome_logger = "0.8" -fun_time = { version = "0.3.1", features = ["log"] } +fun_time = { version = "0.3", features = ["log"] } czkawka_core = { path = "../czkawka_core", version = "6.1.0", features = [] } gtk4 = { version = "0.7", default-features = false, features = ["v4_6"] } diff --git a/czkawka_gui/src/compute_results.rs b/czkawka_gui/src/compute_results.rs index 70b4b43..f10f85b 100644 --- a/czkawka_gui/src/compute_results.rs +++ b/czkawka_gui/src/compute_results.rs @@ -1325,13 +1325,13 @@ fn computer_duplicate_finder( } } -fn vector_sort_unstable_entry_by_path(vector: &Vec) -> Vec { +fn vector_sort_unstable_entry_by_path(vector: &[FileEntry]) -> Vec { if vector.len() >= 2 { - let mut vector = vector.clone(); + let mut vector = vector.to_owned(); vector.sort_unstable_by(|a, b| split_path_compare(a.path.as_path(), b.path.as_path())); vector } else { - vector.clone() + vector.to_owned() } } diff --git a/czkawka_gui/src/main.rs b/czkawka_gui/src/main.rs index e685317..0601866 100644 --- a/czkawka_gui/src/main.rs +++ b/czkawka_gui/src/main.rs @@ -83,6 +83,8 @@ fn build_ui(application: &Application, arguments: &[OsString]) { let gui_data: GuiData = GuiData::new_with_application(application); // Used for getting data from thread + // TODO - deprecation happened without any example, so not sure how new code should look like + #[allow(deprecated)] let (glib_stop_sender, glib_stop_receiver) = glib::MainContext::channel(Priority::default()); // Futures progress report diff --git a/czkawka_gui/src/saving_loading.rs b/czkawka_gui/src/saving_loading.rs index cd81d87..ffc05c8 100644 --- a/czkawka_gui/src/saving_loading.rs +++ b/czkawka_gui/src/saving_loading.rs @@ -257,7 +257,7 @@ impl LoadSaveStruct { }; return Some((config_file_handler, config_file)); } - if !config_file.exists() || !config_file.is_file() { + if !config_file.is_file() { if manual_execution { // Don't show errors when there is no configuration file when starting app add_text_to_text_view( diff --git a/krokiet/Cargo.toml b/krokiet/Cargo.toml index d92d9ba..2ae6ecf 100644 --- a/krokiet/Cargo.toml +++ b/krokiet/Cargo.toml @@ -40,8 +40,8 @@ rayon = "1.8.0" # Translations i18n-embed = { version = "0.14", features = ["fluent-system", "desktop-requester"] } i18n-embed-fl = "0.7" -rust-embed = { version = "8.0", features = ["debug-embed"] } -once_cell = "1.18" +rust-embed = { version = "8.1", features = ["debug-embed"] } +once_cell = "1.19" [build-dependencies] slint-build = "1.3" diff --git a/krokiet/src/connect_scan.rs b/krokiet/src/connect_scan.rs index 4f9a3c4..834a7f7 100644 --- a/krokiet/src/connect_scan.rs +++ b/krokiet/src/connect_scan.rs @@ -3,11 +3,12 @@ use crate::{CurrentTab, GuiState, MainListModel, MainWindow, ProgressToSend}; use chrono::NaiveDateTime; use crossbeam_channel::{Receiver, Sender}; use czkawka_core::common::{split_path, split_path_compare, DEFAULT_THREAD_SIZE}; -use czkawka_core::common_dir_traversal::{FileEntry, FolderEntry, ProgressData}; +use czkawka_core::common_dir_traversal::{FileEntry, ProgressData}; use czkawka_core::common_tool::CommonData; use czkawka_core::common_traits::ResultEntry; use czkawka_core::empty_files::EmptyFiles; use czkawka_core::empty_folder::EmptyFolder; +use czkawka_core::empty_folder::FolderEntry; use czkawka_core::similar_images; use czkawka_core::similar_images::SimilarImages; use humansize::{format_size, BINARY}; diff --git a/krokiet/src/main.rs b/krokiet/src/main.rs index 612ffcd..dd2faee 100644 --- a/krokiet/src/main.rs +++ b/krokiet/src/main.rs @@ -31,6 +31,8 @@ mod set_initial_gui_info; mod settings; use crossbeam_channel::{unbounded, Receiver, Sender}; +use slint::VecModel; +use std::rc::Rc; // use std::rc::Rc; use crate::connect_delete::connect_delete_button; @@ -60,6 +62,10 @@ fn main() { // to_remove_debug(&app); + // Slint files may already contains data in models, so clear them before starting - todo, + // check if non zeroed models are useful + zeroing_all_models(&app); + set_initial_gui_infos(&app); create_default_settings_files(); @@ -80,6 +86,12 @@ fn main() { save_all_settings_to_file(&app); } +pub fn zeroing_all_models(app: &MainWindow) { + app.set_empty_folder_model(Rc::new(VecModel::default()).into()); + app.set_empty_files_model(Rc::new(VecModel::default()).into()); + app.set_similar_images_model(Rc::new(VecModel::default()).into()); +} + // // TODO remove this after debugging - or leave commented // pub fn to_remove_debug(app: &MainWindow) { // app.set_empty_folder_model(to_remove_create_without_header("@@").into());