From f6971562bbd9c5162337c31e77c15550d7ad8872 Mon Sep 17 00:00:00 2001 From: Jay Date: Fri, 2 Oct 2020 14:39:08 +0100 Subject: [PATCH 01/22] Speed optimisations for recursive-heavy tasks This commit makes some changes based on analysing the stack trace with `cargo flamegraph`. All changes are within the creation of the `Meta` struct since this takes up most of the processing time. Some heavy operations, namely memory allocations and system calls, have been either reduced or deferred until the information is needed, significantly speeding up the `-R` and `--tree` options. This commit also simplifies some code and makes the repo ignore `Cargo.lock` since it is not needed for binary projects. --- .gitignore | 1 + Cargo.lock | 731 ---------------------------------------- Cargo.toml | 38 +-- src/app.rs | 23 +- src/core.rs | 17 +- src/display.rs | 11 +- src/icon.rs | 4 +- src/main.rs | 3 +- src/meta/date.rs | 68 ++-- src/meta/mod.rs | 37 +- src/meta/name.rs | 154 ++++----- src/meta/owner.rs | 62 ++-- src/meta/permissions.rs | 8 +- src/meta/symlink.rs | 50 +-- src/sort.rs | 2 +- 15 files changed, 208 insertions(+), 1001 deletions(-) delete mode 100644 Cargo.lock diff --git a/.gitignore b/.gitignore index 4888cb786..f9019a38e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /target out.md **/*.rs.bk +Cargo.lock diff --git a/Cargo.lock b/Cargo.lock deleted file mode 100644 index def2e6e8b..000000000 --- a/Cargo.lock +++ /dev/null @@ -1,731 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -[[package]] -name = "aho-corasick" -version = "0.7.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "043164d8ba5c4c3035fec9bbee8647c0261d788f3474306f93bb65901cae0e86" -dependencies = [ - "memchr", -] - -[[package]] -name = "ansi_term" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" -dependencies = [ - "winapi", -] - -[[package]] -name = "ansi_term" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" -dependencies = [ - "winapi", -] - -[[package]] -name = "arrayref" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" - -[[package]] -name = "arrayvec" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8" - -[[package]] -name = "assert_cmd" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c88b9ca26f9c16ec830350d309397e74ee9abdfd8eb1f71cb6ecc71a3fc818da" -dependencies = [ - "doc-comment", - "predicates", - "predicates-core", - "predicates-tree", - "wait-timeout", -] - -[[package]] -name = "assert_fs" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04dabd011e19821a348abb0dec7b7fda959cd6b3477c474395b958b291942b0e" -dependencies = [ - "doc-comment", - "globwalk", - "predicates", - "predicates-core", - "predicates-tree", - "tempfile", -] - -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi", - "libc", - "winapi", -] - -[[package]] -name = "autocfg" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" - -[[package]] -name = "base64" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" - -[[package]] -name = "bitflags" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" - -[[package]] -name = "blake2b_simd" -version = "0.5.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8fb2d74254a3a0b5cac33ac9f8ed0e44aa50378d9dbb2e5d83bd21ed1dc2c8a" -dependencies = [ - "arrayref", - "arrayvec", - "constant_time_eq", -] - -[[package]] -name = "bstr" -version = "0.2.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31accafdb70df7871592c058eca3985b71104e15ac32f64706022c58867da931" -dependencies = [ - "memchr", -] - -[[package]] -name = "cfg-if" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" - -[[package]] -name = "chrono" -version = "0.4.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "942f72db697d8767c22d46a598e01f2d3b475501ea43d0db4f16d90259182d0b" -dependencies = [ - "num-integer", - "num-traits", - "time", -] - -[[package]] -name = "chrono-humanize" -version = "0.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb2ff48a655fe8d2dae9a39e66af7fd8ff32a879e8c4e27422c25596a8b5e90d" -dependencies = [ - "chrono", -] - -[[package]] -name = "clap" -version = "2.33.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" -dependencies = [ - "ansi_term 0.11.0", - "atty", - "bitflags", - "strsim", - "term_size", - "textwrap", - "unicode-width", - "vec_map", -] - -[[package]] -name = "constant_time_eq" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" - -[[package]] -name = "crossbeam-utils" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" -dependencies = [ - "autocfg", - "cfg-if", - "lazy_static", -] - -[[package]] -name = "difference" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198" - -[[package]] -name = "dirs" -version = "3.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "142995ed02755914747cc6ca76fc7e4583cd18578746716d0508ea6ed558b9ff" -dependencies = [ - "dirs-sys", -] - -[[package]] -name = "dirs-sys" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e93d7f5705de3e49895a2b5e0b8855a1c27f080192ae9c32a6432d50741a57a" -dependencies = [ - "libc", - "redox_users", - "winapi", -] - -[[package]] -name = "doc-comment" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" - -[[package]] -name = "float-cmp" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1267f4ac4f343772758f7b1bdcbe767c218bbab93bb432acbf5162bbf85a6c4" -dependencies = [ - "num-traits", -] - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "getrandom" -version = "0.1.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc587bc0ec293155d5bfa6b9891ec18a1e330c234f896ea47fbada4cadbe47e6" -dependencies = [ - "cfg-if", - "libc", - "wasi 0.9.0+wasi-snapshot-preview1", -] - -[[package]] -name = "glob" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" - -[[package]] -name = "globset" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ad1da430bd7281dde2576f44c84cc3f0f7b475e7202cd503042dff01a8c8120" -dependencies = [ - "aho-corasick", - "bstr", - "fnv", - "log", - "regex", -] - -[[package]] -name = "globwalk" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9db17aec586697a93219b19726b5b68307eba92898c34b170857343fe67c99d" -dependencies = [ - "ignore", - "walkdir", -] - -[[package]] -name = "hermit-abi" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c30f6d0bc6b00693347368a67d41b58f2fb851215ff1da49e90fe2c5c667151" -dependencies = [ - "libc", -] - -[[package]] -name = "human-sort" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "140a09c9305e6d5e557e2ed7cbc68e05765a7d4213975b87cb04920689cc6219" - -[[package]] -name = "ignore" -version = "0.4.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22dcbf2a4a289528dbef21686354904e1c694ac642610a9bff9e7df730d9ec72" -dependencies = [ - "crossbeam-utils", - "globset", - "lazy_static", - "log", - "memchr", - "regex", - "same-file", - "thread_local", - "walkdir", - "winapi-util", -] - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - -[[package]] -name = "libc" -version = "0.2.77" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2f96b10ec2560088a8e76961b00d47107b3a625fecb76dedb29ee7ccbf98235" - -[[package]] -name = "linked-hash-map" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8dd5a6d5999d9907cda8ed67bbd137d3af8085216c2ac62de5be860bd41f304a" - -[[package]] -name = "log" -version = "0.4.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "lscolors" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d24b894c45c9da468621cdd615a5a79ee5e5523dd4f75c76ebc03d458940c16e" -dependencies = [ - "ansi_term 0.12.1", -] - -[[package]] -name = "lsd" -version = "0.18.0" -dependencies = [ - "ansi_term 0.12.1", - "assert_cmd", - "assert_fs", - "chrono-humanize", - "clap", - "dirs", - "globset", - "human-sort", - "libc", - "lscolors", - "predicates", - "tempfile", - "term_grid", - "terminal_size", - "time", - "unicode-width", - "users", - "version_check", - "wild", - "winapi", - "xdg", - "yaml-rust", -] - -[[package]] -name = "memchr" -version = "2.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" - -[[package]] -name = "normalize-line-endings" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" - -[[package]] -name = "num-integer" -version = "0.1.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d59457e662d541ba17869cf51cf177c0b5f0cbf476c66bdc90bf1edac4f875b" -dependencies = [ - "autocfg", - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac267bcc07f48ee5f8935ab0d24f316fb722d7a1292e2913f0cc196b29ffd611" -dependencies = [ - "autocfg", -] - -[[package]] -name = "ppv-lite86" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c36fa947111f5c62a733b652544dd0016a43ce89619538a8ef92724a6f501a20" - -[[package]] -name = "predicates" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96bfead12e90dccead362d62bb2c90a5f6fc4584963645bc7f71a735e0b0735a" -dependencies = [ - "difference", - "float-cmp", - "normalize-line-endings", - "predicates-core", - "regex", -] - -[[package]] -name = "predicates-core" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06075c3a3e92559ff8929e7a280684489ea27fe44805174c3ebd9328dcb37178" - -[[package]] -name = "predicates-tree" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e63c4859013b38a76eca2414c64911fba30def9e3202ac461a2d22831220124" -dependencies = [ - "predicates-core", - "treeline", -] - -[[package]] -name = "rand" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" -dependencies = [ - "getrandom", - "libc", - "rand_chacha", - "rand_core", - "rand_hc", -] - -[[package]] -name = "rand_chacha" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" -dependencies = [ - "getrandom", -] - -[[package]] -name = "rand_hc" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" -dependencies = [ - "rand_core", -] - -[[package]] -name = "redox_syscall" -version = "0.1.57" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" - -[[package]] -name = "redox_users" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de0737333e7a9502c789a36d7c7fa6092a49895d4faa31ca5df163857ded2e9d" -dependencies = [ - "getrandom", - "redox_syscall", - "rust-argon2", -] - -[[package]] -name = "regex" -version = "1.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c3780fcf44b193bc4d09f36d2a3c87b251da4a046c87795a0d35f4f927ad8e6" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", - "thread_local", -] - -[[package]] -name = "regex-syntax" -version = "0.6.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26412eb97c6b088a6997e05f69403a802a92d520de2f8e63c2b65f9e0f47c4e8" - -[[package]] -name = "remove_dir_all" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" -dependencies = [ - "winapi", -] - -[[package]] -name = "rust-argon2" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dab61250775933275e84053ac235621dfb739556d5c54a2f2e9313b7cf43a19" -dependencies = [ - "base64", - "blake2b_simd", - "constant_time_eq", - "crossbeam-utils", -] - -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "strsim" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" - -[[package]] -name = "tempfile" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" -dependencies = [ - "cfg-if", - "libc", - "rand", - "redox_syscall", - "remove_dir_all", - "winapi", -] - -[[package]] -name = "term_grid" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "230d3e804faaed5a39b08319efb797783df2fd9671b39b7596490cb486d702cf" -dependencies = [ - "unicode-width", -] - -[[package]] -name = "term_size" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e4129646ca0ed8f45d09b929036bafad5377103edd06e50bf574b353d2b08d9" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "terminal_size" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a14cd9f8c72704232f0bfc8455c0e861f0ad4eb60cc9ec8a170e231414c1e13" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "textwrap" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" -dependencies = [ - "term_size", - "unicode-width", -] - -[[package]] -name = "thread_local" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" -dependencies = [ - "lazy_static", -] - -[[package]] -name = "time" -version = "0.1.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" -dependencies = [ - "libc", - "wasi 0.10.0+wasi-snapshot-preview1", - "winapi", -] - -[[package]] -name = "treeline" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7f741b240f1a48843f9b8e0444fb55fb2a4ff67293b50a9179dfd5ea67f8d41" - -[[package]] -name = "unicode-width" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" - -[[package]] -name = "users" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24cc0f6d6f267b73e5a2cadf007ba8f9bc39c6a6f9666f8cf25ea809a153b032" -dependencies = [ - "libc", - "log", -] - -[[package]] -name = "vec_map" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" - -[[package]] -name = "version_check" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" - -[[package]] -name = "wait-timeout" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" -dependencies = [ - "libc", -] - -[[package]] -name = "walkdir" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "777182bc735b6424e1a57516d35ed72cb8019d85c8c9bf536dccb3445c1a2f7d" -dependencies = [ - "same-file", - "winapi", - "winapi-util", -] - -[[package]] -name = "wasi" -version = "0.9.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" - -[[package]] -name = "wasi" -version = "0.10.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" - -[[package]] -name = "wild" -version = "2.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "035793abb854745033f01a07647a79831eba29ec0be377205f2a25b0aa830020" -dependencies = [ - "glob", -] - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-util" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" -dependencies = [ - "winapi", -] - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "xdg" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a66b7c2281ebde13cf4391d70d4c7e5946c3c25e72a7b859ca8f677dcd0b0c61" - -[[package]] -name = "yaml-rust" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39f0c922f1a334134dc2f7a8b67dc5d25f0735263feec974345ff706bcf20b0d" -dependencies = [ - "linked-hash-map", -] diff --git a/Cargo.toml b/Cargo.toml index 6c66aeed5..27c5cab9f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,35 +16,35 @@ name = "lsd" path = "src/main.rs" [build-dependencies] -clap = "2.33.*" -version_check = "0.9.*" -time = "0.1.*" +clap = "2.33" +version_check = "0.9" +chrono = "0.4" [dependencies] -ansi_term = "0.12.*" -dirs = "3.0.*" -libc = "0.2.*" -human-sort = "0.2.2" -term_grid = "0.1.*" -terminal_size = "0.1.*" -time = "0.1.*" -chrono-humanize = "0.0.*" -unicode-width = "0.1.*" +ansi_term = "0.12" +dirs = "3.0" +libc = "0.2" +human-sort = "0.2" +term_grid = "0.1" +chrono-humanize = "0.0" +unicode-width = "0.1" lscolors = "0.7" -wild = "2.0.*" -globset = "0.4.*" -xdg = "2.1.*" -yaml-rust = "0.4.*" +wild = "2.0" +globset = "0.4" +xdg = "2.1" +yaml-rust = "0.4" +termsize = "0.1" +chrono = "0.4" [target.'cfg(unix)'.dependencies] -users = "0.11.*" +users = "0.11" [target.'cfg(windows)'.dependencies] -winapi = {version = "0.3.*", features = ["aclapi", "accctrl", "winnt", "winerror", "securitybaseapi", "winbase"]} +winapi = {version = "0.3", features = ["aclapi", "accctrl", "winnt", "winerror", "securitybaseapi", "winbase"]} [dependencies.clap] features = ["suggestions", "color", "wrap_help"] -version = "2.33.*" +version = "2.33" [dev-dependencies] assert_cmd = "1" diff --git a/src/app.rs b/src/app.rs index 8597c783d..bd34228b9 100644 --- a/src/app.rs +++ b/src/app.rs @@ -284,7 +284,24 @@ fn validate_date_argument(arg: String) -> Result<(), String> { } } -pub fn validate_time_format(formatter: &str) -> Result<(), time::ParseError> { +#[derive(Debug)] +pub enum FormatError { + InvalidSpecifier(char), + MissingConverter, +} + +impl std::fmt::Display for FormatError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::InvalidSpecifier(c) => write!(f, "invalid format specifier: %{}", c), + Self::MissingConverter => write!(f, "missing format converter"), + } + } +} + +impl std::error::Error for FormatError {} + +pub fn validate_time_format(formatter: &str) -> Result<(), FormatError> { let mut chars = formatter.chars(); loop { match chars.next() { @@ -297,8 +314,8 @@ pub fn validate_time_format(formatter: &str) -> Result<(), time::ParseError> { | Some('U') | Some('u') | Some('V') | Some('v') | Some('W') | Some('w') | Some('X') | Some('x') | Some('Y') | Some('y') | Some('Z') | Some('z') | Some('+') | Some('%') => (), - Some(c) => return Err(time::ParseError::InvalidFormatSpecifier(c)), - None => return Err(time::ParseError::MissingFormatConverter), + Some(c) => return Err(FormatError::InvalidSpecifier(c)), + None => return Err(FormatError::MissingConverter), }, None => break, _ => continue, diff --git a/src/core.rs b/src/core.rs index 92cea8f81..8bab66f1c 100644 --- a/src/core.rs +++ b/src/core.rs @@ -6,14 +6,6 @@ use crate::meta::Meta; use crate::{print_error, print_output, sort}; use std::path::PathBuf; -#[cfg(not(target_os = "windows"))] -use std::io; -#[cfg(not(target_os = "windows"))] -use std::os::unix::io::AsRawFd; - -#[cfg(target_os = "windows")] -use terminal_size::terminal_size; - pub struct Core { flags: Flags, icons: Icons, @@ -24,17 +16,12 @@ pub struct Core { impl Core { pub fn new(flags: Flags) -> Self { - // Check through libc if stdout is a tty. Unix specific so not on windows. - // Determine color output availability (and initialize color output (for Windows 10)) - #[cfg(not(target_os = "windows"))] - let tty_available = unsafe { libc::isatty(io::stdout().as_raw_fd()) == 1 }; + // termsize allows us to know if the stdout is a tty or not. + let tty_available = termsize::get().is_some(); #[cfg(not(target_os = "windows"))] let console_color_ok = true; - #[cfg(target_os = "windows")] - let tty_available = terminal_size().is_some(); // terminal_size allows us to know if the stdout is a tty or not. - #[cfg(target_os = "windows")] let console_color_ok = ansi_term::enable_ansi_support().is_ok(); diff --git a/src/display.rs b/src/display.rs index 3d1738093..71a773f06 100644 --- a/src/display.rs +++ b/src/display.rs @@ -1,12 +1,10 @@ use crate::color::{ColoredString, Colors}; use crate::flags::{Block, Display, Flags, Layout}; use crate::icon::Icons; -use crate::meta::name::DisplayOption; -use crate::meta::{FileType, Meta}; +use crate::meta::{DisplayOption, FileType, Meta}; use ansi_term::{ANSIString, ANSIStrings}; use std::collections::HashMap; use term_grid::{Cell, Direction, Filling, Grid, GridOptions}; -use terminal_size::terminal_size; use unicode_width::UnicodeWidthStr; const EDGE: &str = "\u{251c}\u{2500}\u{2500}"; // "├──" @@ -15,11 +13,6 @@ const CORNER: &str = "\u{2514}\u{2500}\u{2500}"; // "└──" const BLANK: &str = " "; pub fn grid(metas: &[Meta], flags: &Flags, colors: &Colors, icons: &Icons) -> String { - let term_width = match terminal_size() { - Some((w, _)) => Some(w.0 as usize), - None => None, - }; - inner_display_grid( &DisplayOption::None, metas, @@ -27,7 +20,7 @@ pub fn grid(metas: &[Meta], flags: &Flags, colors: &Colors, icons: &Icons) -> St colors, icons, 0, - term_width, + termsize::get().map(|size| size.cols as usize), ) } diff --git a/src/icon.rs b/src/icon.rs index f166c4dd2..42eee2e95 100644 --- a/src/icon.rs +++ b/src/icon.rs @@ -77,13 +77,13 @@ impl Icons { "\u{f2dc}" // "" } else if let Some(icon) = self .icons_by_name - .get(name.file_name().to_lowercase().as_str()) + .get(name.get_name().to_ascii_lowercase().as_str()) { // Use the known names. icon } else if let Some(icon) = name.extension().and_then(|extension| { self.icons_by_extension - .get(extension.to_lowercase().as_str()) + .get(extension.to_ascii_lowercase().as_str()) }) { // Use the known extensions. icon diff --git a/src/main.rs b/src/main.rs index 3f38d5264..6fe585afb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,6 +8,7 @@ #[macro_use] extern crate clap; extern crate ansi_term; +extern crate chrono; extern crate chrono_humanize; extern crate dirs; extern crate libc; @@ -15,8 +16,6 @@ extern crate lscolors; #[cfg(test)] extern crate tempfile; extern crate term_grid; -extern crate terminal_size; -extern crate time; extern crate unicode_width; extern crate wild; extern crate xdg; diff --git a/src/meta/date.rs b/src/meta/date.rs index eca95ecc3..23096f13b 100644 --- a/src/meta/date.rs +++ b/src/meta/date.rs @@ -1,57 +1,46 @@ use crate::color::{ColoredString, Colors, Elem}; use crate::flags::{DateFlag, Flags}; +use chrono::{DateTime, Duration, Local}; use chrono_humanize::HumanTime; use std::fs::Metadata; -use std::time::UNIX_EPOCH; -use time::{Duration, Timespec}; #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] -pub struct Date(time::Tm); +pub struct Date(DateTime); impl<'a> From<&'a Metadata> for Date { fn from(meta: &'a Metadata) -> Self { let modified_time = meta.modified().expect("failed to retrieve modified date"); - - let modified_time_since_epoch = - modified_time.duration_since(UNIX_EPOCH).unwrap_or_default(); - - let time = time::at(Timespec::new( - modified_time_since_epoch.as_secs() as i64, - modified_time_since_epoch.subsec_nanos() as i32, - )); - - Date(time) + Date(modified_time.into()) } } impl Date { pub fn render(&self, colors: &Colors, flags: &Flags) -> ColoredString { - let now = time::now(); + let ago = Local::now() - self.0; - let elem; - if self.0 > now - Duration::hours(1) { - elem = &Elem::HourOld; - } else if self.0 > now - Duration::days(1) { - elem = &Elem::DayOld; + let elem = if ago < Duration::hours(1) { + Elem::HourOld + } else if ago < Duration::days(1) { + Elem::DayOld } else { - elem = &Elem::Older; - } + Elem::Older + }; - colors.colorize(self.date_string(&flags), elem) + colors.colorize(self.date_string(&flags), &elem) } pub fn date_string(&self, flags: &Flags) -> String { match &flags.date { - DateFlag::Date => self.0.ctime().to_string(), - DateFlag::Relative => format!("{}", HumanTime::from(self.0 - time::now())), - DateFlag::Formatted(format) => self.0.to_local().strftime(&format).unwrap().to_string(), + DateFlag::Date => self.0.format("%a %b %e %X %G").to_string(), + DateFlag::Relative => HumanTime::from(self.0 - Local::now()).to_string(), + DateFlag::Formatted(format) => self.0.format(&format).to_string(), } } } #[cfg(test)] mod test { - use super::Date; + use super::{Date, DateTime, Duration, Local}; use crate::color::{Colors, Theme}; use crate::flags::{DateFlag, Flags}; use ansi_term::Colour; @@ -59,19 +48,18 @@ mod test { use std::path::Path; use std::process::{Command, ExitStatus}; use std::{env, fs}; - use time; #[cfg(unix)] - fn cross_platform_touch(path: &Path, date: &time::Tm) -> io::Result { + fn cross_platform_touch(path: &Path, date: &DateTime) -> io::Result { Command::new("touch") .arg("-t") - .arg(date.strftime("%Y%m%d%H%M.%S").unwrap().to_string()) + .arg(date.format("%Y%m%d%H%M.%S").to_string()) .arg(&path) .status() } #[cfg(windows)] - fn cross_platform_touch(path: &Path, date: &time::Tm) -> io::Result { + fn cross_platform_touch(path: &Path, date: &DateTime) -> io::Result { use std::process::Stdio; let copy_success = Command::new("cmd") @@ -90,7 +78,7 @@ mod test { .arg(format!( r#"$(Get-Item {}).lastwritetime=$(Get-Date "{}")"#, path.display(), - date.rfc3339() + date.to_rfc3339() )) .status() } @@ -100,7 +88,7 @@ mod test { let mut file_path = env::temp_dir(); file_path.push("test_an_hour_old_file_color.tmp"); - let creation_date = (time::now() - time::Duration::seconds(4)).to_local(); + let creation_date = Local::now() - Duration::seconds(4); let success = cross_platform_touch(&file_path, &creation_date) .unwrap() @@ -112,7 +100,7 @@ mod test { let flags = Flags::default(); assert_eq!( - Colour::Fixed(40).paint(creation_date.ctime().to_string()), + Colour::Fixed(40).paint(creation_date.format("%a %b %e %X %G").to_string()), date.render(&colors, &flags) ); @@ -124,7 +112,7 @@ mod test { let mut file_path = env::temp_dir(); file_path.push("test_a_day_old_file_color.tmp"); - let creation_date = (time::now() - time::Duration::hours(4)).to_local(); + let creation_date = Local::now() - Duration::hours(4); let success = cross_platform_touch(&file_path, &creation_date) .unwrap() @@ -136,7 +124,7 @@ mod test { let flags = Flags::default(); assert_eq!( - Colour::Fixed(42).paint(creation_date.ctime().to_string()), + Colour::Fixed(42).paint(creation_date.format("%a %b %e %X %G").to_string()), date.render(&colors, &flags) ); @@ -148,9 +136,9 @@ mod test { let mut file_path = env::temp_dir(); file_path.push("test_a_several_days_old_file_color.tmp"); - let creation_date = time::now_utc() - time::Duration::days(2); + let creation_date = Local::now() - Duration::days(2); - let success = cross_platform_touch(&file_path, &creation_date.to_local()) + let success = cross_platform_touch(&file_path, &creation_date) .unwrap() .success(); assert!(success, "failed to exec touch"); @@ -160,7 +148,7 @@ mod test { let flags = Flags::default(); assert_eq!( - Colour::Fixed(36).paint(creation_date.ctime().to_string()), + Colour::Fixed(36).paint(creation_date.format("%a %b %e %X %G").to_string()), date.render(&colors, &flags) ); @@ -172,7 +160,7 @@ mod test { let mut file_path = env::temp_dir(); file_path.push("test_with_relative_date.tmp"); - let creation_date = time::now() - time::Duration::days(2); + let creation_date = Local::now() - Duration::days(2); let success = cross_platform_touch(&file_path, &creation_date) .unwrap() @@ -198,7 +186,7 @@ mod test { let mut file_path = env::temp_dir(); file_path.push("test_with_relative_date_now.tmp"); - let creation_date = time::now(); + let creation_date = Local::now(); let success = cross_platform_touch(&file_path, &creation_date) .unwrap() .success(); diff --git a/src/meta/mod.rs b/src/meta/mod.rs index f1569fbc6..c6a34ee47 100644 --- a/src/meta/mod.rs +++ b/src/meta/mod.rs @@ -2,7 +2,7 @@ mod date; mod filetype; mod indicator; mod inode; -pub mod name; +mod name; mod owner; mod permissions; mod size; @@ -15,7 +15,7 @@ pub use self::date::Date; pub use self::filetype::FileType; pub use self::indicator::Indicator; pub use self::inode::INode; -pub use self::name::Name; +pub use self::name::{DisplayOption, Name}; pub use self::owner::Owner; pub use self::permissions::Permissions; pub use self::size::Size; @@ -26,7 +26,6 @@ use crate::flags::{Display, Flags, Layout}; use crate::print_error; use std::fs::read_link; -use std::io::{Error, ErrorKind}; use std::path::{Component, Path, PathBuf}; #[derive(Clone, Debug)] @@ -50,11 +49,7 @@ impl Meta { depth: usize, flags: &Flags, ) -> Result>, std::io::Error> { - if depth == 0 { - return Ok(None); - } - - if flags.display == Display::DirectoryItself { + if depth == 0 || flags.display == Display::DirectoryItself { return Ok(None); } @@ -79,10 +74,8 @@ impl Meta { let mut content: Vec = Vec::new(); if let Display::All = flags.display { - let mut current_meta; - - current_meta = self.clone(); - current_meta.name.name = ".".to_owned(); + let mut current_meta = self.clone(); + current_meta.name.set_name(".".to_owned()); let parent_meta = Self::from_path(&self.path.join(Component::ParentDir), flags.dereference.0)?; @@ -92,11 +85,8 @@ impl Meta { } for entry in entries { - let path = entry?.path(); - - let name = path - .file_name() - .ok_or_else(|| Error::new(ErrorKind::InvalidInput, "invalid file name"))?; + let entry = entry?; + let name = entry.file_name(); if flags.ignore_globs.0.is_match(&name) { continue; @@ -108,10 +98,10 @@ impl Meta { } } - let mut entry_meta = match Self::from_path(&path, flags.dereference.0) { + let mut entry_meta = match Self::from_path(&entry.path(), flags.dereference.0) { Ok(res) => res, Err(err) => { - print_error!("lsd: {}: {}\n", path.display(), err); + print_error!("lsd: {:?}: {}\n", entry.path(), err); continue; } }; @@ -119,7 +109,7 @@ impl Meta { match entry_meta.recurse_into(depth - 1, &flags) { Ok(content) => entry_meta.content = content, Err(err) => { - print_error!("lsd: {}: {}\n", path.display(), err); + print_error!("lsd: {:?}: {}\n", entry.path(), err); continue; } }; @@ -192,10 +182,9 @@ impl Meta { pub fn from_path(path: &Path, dereference: bool) -> Result { // If the file is a link then retrieve link metadata instead with target metadata (if present). - let (metadata, symlink_meta) = if read_link(path).is_ok() && !dereference { - (path.symlink_metadata()?, path.metadata().ok()) - } else { - (path.metadata()?, None) + let (metadata, symlink_meta) = match path.symlink_metadata() { + Ok(metadata) if !dereference => (metadata, path.metadata().ok()), + _ => (path.metadata()?, None), }; #[cfg(unix)] diff --git a/src/meta/name.rs b/src/meta/name.rs index 3c8c2d79e..d09a1f164 100644 --- a/src/meta/name.rs +++ b/src/meta/name.rs @@ -1,8 +1,9 @@ use crate::color::{ColoredString, Colors, Elem}; use crate::icon::Icons; use crate::meta::filetype::FileType; + +use std::borrow::Cow; use std::cmp::{Ordering, PartialOrd}; -use std::ffi::OsStr; use std::path::{Component, Path, PathBuf}; #[derive(Debug)] @@ -14,86 +15,73 @@ pub enum DisplayOption<'a> { #[derive(Clone, Debug, Eq)] pub struct Name { - pub name: String, + name: Option, path: PathBuf, - extension: Option, file_type: FileType, } impl Name { pub fn new(path: &Path, file_type: FileType) -> Self { - let name = match path.file_name() { - Some(name) => name.to_string_lossy().to_string(), - None => path.to_string_lossy().to_string(), - }; - - let extension = path - .extension() - .map(|ext| ext.to_string_lossy().to_string()); - Self { - name, - path: PathBuf::from(path), - extension, + name: None, + path: path.into(), file_type, } } - pub fn file_name(&self) -> &str { - self.path - .file_name() - .and_then(OsStr::to_str) - .unwrap_or(&self.name) + pub fn extension(&self) -> Option> { + self.path.extension().map(|e| e.to_string_lossy()) } - fn relative_path + Clone>(&self, base_path: T) -> PathBuf { - let base_path = base_path.as_ref(); + pub fn set_name(&mut self, new_name: String) { + self.name = Some(new_name); + } - if self.path == base_path { - return PathBuf::from(AsRef::::as_ref(&Component::CurDir)); + pub fn get_name(&self) -> Cow<'_, str> { + match &self.name { + Some(name) => Cow::from(name), + None => match self.path.file_name() { + Some(name) => name.to_string_lossy(), + None => self.path.to_string_lossy(), + }, } + } - let shared_components: PathBuf = self - .path - .components() - .zip(base_path.components()) - .take_while(|(target_component, base_component)| target_component == base_component) - .map(|tuple| tuple.0) - .collect(); - - base_path - .strip_prefix(&shared_components) - .unwrap() - .components() - .map(|_| Component::ParentDir) - .chain( - self.path - .strip_prefix(&shared_components) - .unwrap() - .components(), - ) - .collect() + pub fn file_type(&self) -> FileType { + self.file_type } - pub fn escape(&self, string: &str) -> String { - if string - .chars() - .all(|c| c >= 0x20 as char && c != 0x7f as char) - { - string.to_string() + fn relative_path(&self, base_path: &Path) -> PathBuf { + if self.path == base_path { + PathBuf::from(AsRef::::as_ref(&Component::CurDir)) + } else if let Some(prefix) = self.path.ancestors().find(|a| base_path.starts_with(a)) { + std::iter::repeat(Component::ParentDir) + .take( + base_path + .strip_prefix(&prefix) + .unwrap() + .components() + .count(), + ) + .chain(self.path.strip_prefix(&prefix).unwrap().components()) + .collect() } else { - let mut chars = String::new(); - for c in string.chars() { - // The `escape_default` method on `char` is *almost* what we want here, but - // it still escapes non-ASCII UTF-8 characters, which are still printable. - if c >= 0x20 as char && c != 0x7f as char { - chars.push(c); - } else { - chars += &c.escape_default().collect::(); - } + PathBuf::new() + } + } + + fn escape(&self, string: &str) -> String { + let mut chars = String::with_capacity(string.len()); + for c in string.chars() { + // The `escape_default` method on `char` is *almost* what we want here, but + // it still escapes non-ASCII UTF-8 characters, which are still printable. + if c >= 0x20 as char && c != 0x7f as char { + chars.push(c); + } else { + chars += &c.escape_default().collect::(); } - chars } + chars } pub fn render( @@ -102,21 +90,12 @@ impl Name { icons: &Icons, display_option: &DisplayOption, ) -> ColoredString { - let content = match display_option { - DisplayOption::FileName => { - format!("{}{}", icons.get(self), self.escape(self.file_name())) - } - DisplayOption::Relative { base_path } => format!( - "{}{}", - icons.get(self), - self.escape(&self.relative_path(base_path).to_string_lossy()) - ), - DisplayOption::None => format!( - "{}{}", - icons.get(self), - self.escape(&self.path.to_string_lossy()) - ), + let name: String = if let DisplayOption::Relative { base_path } = display_option { + self.escape(&self.relative_path(base_path).to_string_lossy()) + } else { + self.escape(&self.get_name()) }; + let content = format!("{}{}", icons.get(self), name); let elem = match self.file_type { FileType::CharDevice => Elem::CharDevice, @@ -131,33 +110,25 @@ impl Name { colors.colorize_using_path(content, &self.path, &elem) } - - pub fn extension(&self) -> Option<&str> { - self.extension.as_deref() - } - - pub fn file_type(&self) -> FileType { - self.file_type - } } impl Ord for Name { fn cmp(&self, other: &Self) -> Ordering { - self.name.to_lowercase().cmp(&other.name.to_lowercase()) + self.get_name() + .to_ascii_lowercase() + .cmp(&other.get_name().to_ascii_lowercase()) } } impl PartialOrd for Name { fn partial_cmp(&self, other: &Self) -> Option { - self.name - .to_lowercase() - .partial_cmp(&other.name.to_lowercase()) + Some(self.cmp(other)) } } impl PartialEq for Name { fn eq(&self, other: &Self) -> bool { - self.name.eq_ignore_ascii_case(&other.name.to_lowercase()) + self.get_name().eq_ignore_ascii_case(&other.get_name()) } } @@ -335,7 +306,10 @@ mod test { }, ); - assert_eq!(Some("txt"), name.extension()); + assert_eq!( + Some("txt".to_string()), + name.extension().map(|c| c.to_string()) + ); } #[test] @@ -496,7 +470,7 @@ mod test { ); let base_path = PathBuf::from("/home/parent1"); - assert_eq!(PathBuf::from("child"), name.relative_path(base_path),) + assert_eq!(PathBuf::from("child"), name.relative_path(&base_path),) } #[test] @@ -512,7 +486,7 @@ mod test { assert_eq!( PathBuf::from("../../grand-parent1/parent1/child"), - name.relative_path(base_path), + name.relative_path(&base_path), ) } diff --git a/src/meta/owner.rs b/src/meta/owner.rs index a9ecd5c47..5c1e6febd 100644 --- a/src/meta/owner.rs +++ b/src/meta/owner.rs @@ -1,46 +1,64 @@ use crate::color::{ColoredString, Colors, Elem}; #[cfg(unix)] use std::fs::Metadata; +#[cfg(unix)] +use std::os::unix::fs::MetadataExt; +#[cfg(unix)] #[derive(Clone, Debug)] pub struct Owner { - user: String, - group: String, + user: u32, + group: u32, } -impl Owner { - #[cfg_attr(unix, allow(dead_code))] - pub fn new(user: String, group: String) -> Self { - Self { user, group } - } +#[cfg(windows)] +#[derive(Clone, Debug)] +pub struct Owner { + user: String, + group: String, } #[cfg(unix)] -impl<'a> From<&'a Metadata> for Owner { +impl From<&Metadata> for Owner { fn from(meta: &Metadata) -> Self { - use std::os::unix::fs::MetadataExt; - use users::{get_group_by_gid, get_user_by_uid}; - - let user = match get_user_by_uid(meta.uid()) { - Some(res) => res.name().to_string_lossy().to_string(), - None => meta.uid().to_string(), - }; - - let group = match get_group_by_gid(meta.gid()) { - Some(res) => res.name().to_string_lossy().to_string(), - None => meta.gid().to_string(), - }; - - Self { user, group } + Self { + user: meta.uid(), + group: meta.gid(), + } } } impl Owner { + #[cfg(windows)] + pub fn new(user: String, group: String) -> Self { + Self { user, group } + } + + #[cfg(windows)] pub fn render_user(&self, colors: &Colors) -> ColoredString { colors.colorize(self.user.clone(), &Elem::User) } + #[cfg(windows)] pub fn render_group(&self, colors: &Colors) -> ColoredString { colors.colorize(self.group.clone(), &Elem::Group) } + + #[cfg(unix)] + pub fn render_user(&self, colors: &Colors) -> ColoredString { + let user = match users::get_user_by_uid(self.user) { + Some(res) => res.name().to_string_lossy().to_string(), + None => self.user.to_string(), + }; + colors.colorize(user, &Elem::User) + } + + #[cfg(unix)] + pub fn render_group(&self, colors: &Colors) -> ColoredString { + let group = match users::get_group_by_gid(self.group) { + Some(res) => res.name().to_string_lossy().to_string(), + None => self.group.to_string(), + }; + colors.colorize(group, &Elem::Group) + } } diff --git a/src/meta/permissions.rs b/src/meta/permissions.rs index 4b3450a79..a4d8ffb32 100644 --- a/src/meta/permissions.rs +++ b/src/meta/permissions.rs @@ -1,5 +1,6 @@ use crate::color::{ColoredString, Colors, Elem}; use ansi_term::ANSIStrings; +#[cfg(unix)] use std::fs::Metadata; #[derive(Debug, PartialEq, Eq, Copy, Clone)] @@ -21,8 +22,8 @@ pub struct Permissions { pub setuid: bool, } +#[cfg(unix)] impl<'a> From<&'a Metadata> for Permissions { - #[cfg(unix)] fn from(meta: &Metadata) -> Self { use std::os::unix::fs::PermissionsExt; @@ -47,11 +48,6 @@ impl<'a> From<&'a Metadata> for Permissions { setuid: has_bit(modes::SETUID), } } - - #[cfg(windows)] - fn from(_: &Metadata) -> Self { - panic!("Cannot get permissions from metadata on Windows") - } } impl Permissions { diff --git a/src/meta/symlink.rs b/src/meta/symlink.rs index e7d52fd79..346488ec4 100644 --- a/src/meta/symlink.rs +++ b/src/meta/symlink.rs @@ -1,55 +1,31 @@ use crate::color::{ColoredString, Colors, Elem}; use crate::flags::Flags; use ansi_term::{ANSIString, ANSIStrings}; -use std::fs::read_link; use std::path::Path; -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Default)] pub struct SymLink { target: Option, valid: bool, } -impl<'a> From<&'a Path> for SymLink { - fn from(path: &'a Path) -> Self { - if let Ok(target) = read_link(path) { - if target.is_absolute() || path.parent() == None { - return Self { - valid: target.exists(), - target: Some( - target - .to_str() - .expect("failed to convert symlink to str") - .to_string(), - ), - }; - } - - return Self { - target: Some( - target - .to_str() - .expect("failed to convert symlink to str") - .to_string(), - ), - valid: path.parent().unwrap().join(target).exists(), - }; - } - - Self { - target: None, - valid: false, - } +impl From<&Path> for SymLink { + fn from(path: &Path) -> Self { + path.read_link() + .map(|target| Self { + target: Some(target.to_string_lossy().into()), + valid: match (path.parent(), target.is_absolute()) { + (Some(p), false) => p.join(target).exists(), + _ => target.exists(), + }, + }) + .unwrap_or_default() } } impl SymLink { pub fn symlink_string(&self) -> Option { - if let Some(ref target) = self.target { - Some(target.to_string()) - } else { - None - } + self.target.as_ref().map(String::to_string) } pub fn render(&self, colors: &Colors, flag: &Flags) -> ColoredString { diff --git a/src/sort.rs b/src/sort.rs index 99812e588..0aab4fa12 100644 --- a/src/sort.rs +++ b/src/sort.rs @@ -59,7 +59,7 @@ fn by_date(a: &Meta, b: &Meta) -> Ordering { } fn by_version(a: &Meta, b: &Meta) -> Ordering { - compare(&a.name.name, &b.name.name) + compare(&a.name.get_name(), &b.name.get_name()) } fn by_extension(a: &Meta, b: &Meta) -> Ordering { From fbb347d0a4e0ee477089a851c5c745c47a27fbbd Mon Sep 17 00:00:00 2001 From: 0jdxt <4650251+0jdxt@users.noreply.github.com> Date: Sat, 14 Nov 2020 02:05:24 +0000 Subject: [PATCH 02/22] fix tests --- tests/integration.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/integration.rs b/tests/integration.rs index 8dbf5023b..6c137c59a 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -17,6 +17,7 @@ fn test_runs_okay() { #[test] fn test_list_empty_directory() { cmd() + .arg("--oneline") .arg("--ignore-config") .arg(tempdir().path()) .assert() @@ -28,6 +29,7 @@ fn test_list_almost_all_empty_directory() { let matched = ""; cmd() .arg("--almost-all") + .arg("--oneline") .arg("--ignore-config") .arg(tempdir().path()) .assert() @@ -35,6 +37,7 @@ fn test_list_almost_all_empty_directory() { cmd() .arg("-A") + .arg("--oneline") .arg("--ignore-config") .arg(tempdir().path()) .assert() @@ -46,6 +49,7 @@ fn test_list_all_empty_directory() { let matched = "\\.\n\\.\\.\n$"; cmd() .arg("--all") + .arg("--oneline") .arg("--ignore-config") .arg(tempdir().path()) .assert() @@ -53,6 +57,7 @@ fn test_list_all_empty_directory() { cmd() .arg("-a") + .arg("--oneline") .arg("--ignore-config") .arg(tempdir().path()) .assert() @@ -65,6 +70,7 @@ fn test_list_populated_directory() { dir.child("one").touch().unwrap(); dir.child("two").touch().unwrap(); cmd() + .arg("--oneline") .arg("--ignore-config") .arg(dir.path()) .assert() @@ -77,6 +83,7 @@ fn test_list_almost_all_populated_directory() { dir.child("one").touch().unwrap(); dir.child("two").touch().unwrap(); cmd() + .arg("--oneline") .arg("--almost-all") .arg("--ignore-config") .arg(dir.path()) @@ -91,6 +98,7 @@ fn test_list_all_populated_directory() { dir.child("two").touch().unwrap(); cmd() .arg("--all") + .arg("--oneline") .arg("--ignore-config") .arg(dir.path()) .assert() @@ -319,6 +327,7 @@ fn test_version_sort() { dir.child("22").touch().unwrap(); cmd() .arg("-v") + .arg("--oneline") .arg("--ignore-config") .arg(dir.path()) .assert() @@ -335,6 +344,7 @@ fn test_version_sort_overwrite_by_timesort() { cmd() .arg("-v") .arg("-t") + .arg("--oneline") .arg("--ignore-config") .arg(dir.path()) .assert() From 3a1a49f13e9c238ddfc7b981585ea71e5e3acab0 Mon Sep 17 00:00:00 2001 From: 0jdxt <4650251+0jdxt@users.noreply.github.com> Date: Sat, 14 Nov 2020 02:16:58 +0000 Subject: [PATCH 03/22] Restore integration.rs --- tests/integration.rs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/tests/integration.rs b/tests/integration.rs index 6c137c59a..8dbf5023b 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -17,7 +17,6 @@ fn test_runs_okay() { #[test] fn test_list_empty_directory() { cmd() - .arg("--oneline") .arg("--ignore-config") .arg(tempdir().path()) .assert() @@ -29,7 +28,6 @@ fn test_list_almost_all_empty_directory() { let matched = ""; cmd() .arg("--almost-all") - .arg("--oneline") .arg("--ignore-config") .arg(tempdir().path()) .assert() @@ -37,7 +35,6 @@ fn test_list_almost_all_empty_directory() { cmd() .arg("-A") - .arg("--oneline") .arg("--ignore-config") .arg(tempdir().path()) .assert() @@ -49,7 +46,6 @@ fn test_list_all_empty_directory() { let matched = "\\.\n\\.\\.\n$"; cmd() .arg("--all") - .arg("--oneline") .arg("--ignore-config") .arg(tempdir().path()) .assert() @@ -57,7 +53,6 @@ fn test_list_all_empty_directory() { cmd() .arg("-a") - .arg("--oneline") .arg("--ignore-config") .arg(tempdir().path()) .assert() @@ -70,7 +65,6 @@ fn test_list_populated_directory() { dir.child("one").touch().unwrap(); dir.child("two").touch().unwrap(); cmd() - .arg("--oneline") .arg("--ignore-config") .arg(dir.path()) .assert() @@ -83,7 +77,6 @@ fn test_list_almost_all_populated_directory() { dir.child("one").touch().unwrap(); dir.child("two").touch().unwrap(); cmd() - .arg("--oneline") .arg("--almost-all") .arg("--ignore-config") .arg(dir.path()) @@ -98,7 +91,6 @@ fn test_list_all_populated_directory() { dir.child("two").touch().unwrap(); cmd() .arg("--all") - .arg("--oneline") .arg("--ignore-config") .arg(dir.path()) .assert() @@ -327,7 +319,6 @@ fn test_version_sort() { dir.child("22").touch().unwrap(); cmd() .arg("-v") - .arg("--oneline") .arg("--ignore-config") .arg(dir.path()) .assert() @@ -344,7 +335,6 @@ fn test_version_sort_overwrite_by_timesort() { cmd() .arg("-v") .arg("-t") - .arg("--oneline") .arg("--ignore-config") .arg(dir.path()) .assert() From 5d70388c83b2aec5b4581719060ca7607d8c55bb Mon Sep 17 00:00:00 2001 From: 0jdxt <4650251+0jdxt@users.noreply.github.com> Date: Sat, 14 Nov 2020 02:18:24 +0000 Subject: [PATCH 04/22] fix - to be squashed --- src/core.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core.rs b/src/core.rs index 8bab66f1c..fec58ff0d 100644 --- a/src/core.rs +++ b/src/core.rs @@ -17,7 +17,7 @@ pub struct Core { impl Core { pub fn new(flags: Flags) -> Self { // termsize allows us to know if the stdout is a tty or not. - let tty_available = termsize::get().is_some(); + let tty_available = termize::get().is_some(); #[cfg(not(target_os = "windows"))] let console_color_ok = true; From 0537b624a15829e91ea523c5df3ada6632dbf8c7 Mon Sep 17 00:00:00 2001 From: 0jdxt <4650251+0jdxt@users.noreply.github.com> Date: Sat, 14 Nov 2020 02:18:47 +0000 Subject: [PATCH 05/22] fix - to be squashed --- src/display.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/display.rs b/src/display.rs index 71a773f06..46017fcbf 100644 --- a/src/display.rs +++ b/src/display.rs @@ -20,7 +20,7 @@ pub fn grid(metas: &[Meta], flags: &Flags, colors: &Colors, icons: &Icons) -> St colors, icons, 0, - termsize::get().map(|size| size.cols as usize), + termize::get().map(|size| size.0 as usize), ) } From 7860daa0e45e0689a0244fecf2bc5da4c303f8c0 Mon Sep 17 00:00:00 2001 From: 0jdxt <4650251+0jdxt@users.noreply.github.com> Date: Sat, 14 Nov 2020 02:19:33 +0000 Subject: [PATCH 06/22] fix - to be squashed --- src/display.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/display.rs b/src/display.rs index 46017fcbf..46c386769 100644 --- a/src/display.rs +++ b/src/display.rs @@ -20,7 +20,7 @@ pub fn grid(metas: &[Meta], flags: &Flags, colors: &Colors, icons: &Icons) -> St colors, icons, 0, - termize::get().map(|size| size.0 as usize), + termize::dimensions().map(|size| size.0 as usize), ) } From 1bc40eda05816882be0c289cefe2b615d942595b Mon Sep 17 00:00:00 2001 From: 0jdxt <4650251+0jdxt@users.noreply.github.com> Date: Sat, 14 Nov 2020 02:19:54 +0000 Subject: [PATCH 07/22] fix - to be squashed --- src/core.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core.rs b/src/core.rs index fec58ff0d..b78067166 100644 --- a/src/core.rs +++ b/src/core.rs @@ -17,7 +17,7 @@ pub struct Core { impl Core { pub fn new(flags: Flags) -> Self { // termsize allows us to know if the stdout is a tty or not. - let tty_available = termize::get().is_some(); + let tty_available = termize::dimensions().is_some(); #[cfg(not(target_os = "windows"))] let console_color_ok = true; From e8155a218f7e8c407c60cc934736eb3533e8f8ae Mon Sep 17 00:00:00 2001 From: 0jdxt <4650251+0jdxt@users.noreply.github.com> Date: Sat, 14 Nov 2020 02:20:27 +0000 Subject: [PATCH 08/22] fix - to be squashed --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 27c5cab9f..0c7809afd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,7 +33,7 @@ wild = "2.0" globset = "0.4" xdg = "2.1" yaml-rust = "0.4" -termsize = "0.1" +termize = "0.1" chrono = "0.4" [target.'cfg(unix)'.dependencies] From 15e7c882e697259c0406d5eceae1be6b68e0aa6e Mon Sep 17 00:00:00 2001 From: Jay Date: Thu, 18 Mar 2021 15:48:38 +0000 Subject: [PATCH 09/22] general speed up this pr reduces syscalls, especially repeated ones. We should only get the metadata once per file. Reduce allocating extra data, especially when not used. --- Cargo.lock | 24 +- Cargo.toml | 3 +- src/color.rs | 23 +- src/core.rs | 21 +- src/display.rs | 175 +++++----- src/icon.rs | 734 +++++++++++++++++++++------------------- src/main.rs | 1 - src/meta/date.rs | 42 +-- src/meta/inode.rs | 27 +- src/meta/mod.rs | 79 +++-- src/meta/name.rs | 209 ++++++------ src/meta/owner.rs | 47 ++- src/meta/permissions.rs | 8 +- src/meta/symlink.rs | 66 ++-- src/sort.rs | 2 +- tests/integration.rs | 6 + 16 files changed, 733 insertions(+), 734 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b18797588..ea4585050 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -115,6 +115,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + [[package]] name = "cfg-if" version = "0.1.10" @@ -233,6 +239,15 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", +] + [[package]] name = "getrandom" version = "0.1.15" @@ -371,6 +386,7 @@ dependencies = [ "chrono-humanize", "clap", "dirs", + "fxhash", "globset", "human-sort", "libc", @@ -381,7 +397,7 @@ dependencies = [ "serial_test", "tempfile", "term_grid", - "terminal_size", + "termize", "unicode-width", "users", "version_check", @@ -723,10 +739,10 @@ dependencies = [ ] [[package]] -name = "terminal_size" -version = "0.1.13" +name = "termize" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a14cd9f8c72704232f0bfc8455c0e861f0ad4eb60cc9ec8a170e231414c1e13" +checksum = "1706be6b564323ce7092f5f7e6b118a14c8ef7ed0e69c8c5329c914a9f101295" dependencies = [ "libc", "winapi", diff --git a/Cargo.toml b/Cargo.toml index eb33d3643..2f98768b6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,7 +25,6 @@ dirs = "3.0.*" libc = "0.2.*" human-sort = "0.2.2" term_grid = "0.1.*" -terminal_size = "0.1.*" chrono = "0.4.*" chrono-humanize = "0.1.*" unicode-width = "0.1.*" @@ -34,6 +33,8 @@ wild = "2.0.*" globset = "0.4.*" xdg = "2.1.*" yaml-rust = "0.4.*" +termize = "0.1" +fxhash = "0.2.*" serde = { version = "1.0", features = ["derive"] } serde_yaml = "0.8" diff --git a/src/color.rs b/src/color.rs index 5670e334f..ecaf755b8 100644 --- a/src/color.rs +++ b/src/color.rs @@ -1,6 +1,7 @@ use ansi_term::{ANSIString, Colour, Style}; use lscolors::{Indicator, LsColors}; use std::collections::HashMap; +use std::fs::Metadata; use std::path::Path; #[allow(dead_code)] @@ -102,19 +103,19 @@ impl Colors { input: String, path: &Path, elem: &Elem, + meta: &Metadata, ) -> ColoredString<'a> { - let style_from_path = self.style_from_path(path); - match style_from_path { - Some(style_from_path) => style_from_path.paint(input), + match self.style_from_path(path, meta) { + Some(style) => style.paint(input), None => self.colorize(input, elem), } } - fn style_from_path(&self, path: &Path) -> Option