Compare commits

..

52 Commits

Author SHA1 Message Date
andy.boot
36fd75b9ee Update packages 2021-01-16 15:12:52 +00:00
andy.boot
51a0ccb1ed Increment version 2021-01-16 15:05:33 +00:00
andy.boot
68b0dd5562 [core] New flag: width
Add support for width flag
https://github.com/bootandy/dust/issues/126

Requested because some people may cat the output

All terminal height/width detection is now in the main file. One method
now has too many args for clippy, this complaint is valid and in the
future we should consider pulling these out into a separate object.
2021-01-16 15:01:42 +00:00
andy.boot
360143ee91 Notes on how to publish 2021-01-16 14:56:45 +00:00
Alexandru Macovei
998e7fb2f8 Address clippy lints 2021-01-16 14:56:14 +00:00
Alexandru Macovei
a48c7782ac Print correct message when paths are not found 2021-01-16 14:56:14 +00:00
andy.boot
e0347b0b43 Fix: Stop adding extra output lines for multi args
If we provided n names we would previous output screen_height + n lines.
This is wrong. We should only print screen_height lines
2020-11-25 13:46:48 +00:00
andy.boot
5b6b449cbd Upgrade packages 2020-11-25 13:46:48 +00:00
andy.boot
c8568e5674 Code cleanup from previous commit 2020-10-21 22:16:40 +01:00
Tyler Bailey
9d13994526 Cleanup (#121)
* remove unneeded identity .map()

* remove redunant field name in struct

* impl num_siblings for Node

* replace nested if w/ match in get_tree_chars

* remove unneeded field name in struct

* remove redundant println! & logic in display_node

* make get_children_from_node a Node method

* Revert "remove redundant println! & logic in display_node"

This reverts commit 40777025d5.
2020-10-21 22:01:18 +01:00
Sylvestre Ledru
ca8b1efc18 Be less prescriptive for the ansi_term dep 2020-10-08 23:04:54 +01:00
oToToT
c261492325 encode non-printable characters
use stfu8 to encode utf-8 characters
2020-10-08 22:57:01 +01:00
Thomas Gotwig
8d43185439 Publish on Homebrew on Linux 🍺🐧🎉 2020-10-03 09:42:27 +01:00
andy.boot
1d018bc80b Add test to check behaviour of -d flag
Covers the case fixed by the previous commit
2020-09-03 22:17:39 +01:00
andy.boot
2319c3032f Increment version for new release 2020-09-03 21:58:27 +01:00
Ryan Winograd
53af0d486d Change behavior of depth flag
Change the depth flag so that it only changes the depth of displayed
subdirectories, not the depth of the directory size calculation (i.e.,
changing --depth does not change the displayed directory size, rather it
only changes how many levels of subdirectories are shown).
2020-09-03 21:47:52 +01:00
andy.boot
b643abe05e Refactor tests.
Split tests up into 2 files, 'exact output match' and 'substring ouput
match'
2020-09-03 00:04:27 +01:00
andy.boot
695c9b6747 Add test to check number of files flag 2020-09-03 00:04:27 +01:00
andy.boot
8e8f18b9bc Rename test_dir3 to test_dir_unicode
This better represents what it is trying to test
2020-09-03 00:04:27 +01:00
andy.boot
e767be217a Add test for hidden flag 2020-09-03 00:04:27 +01:00
andy.boot
9187cb8ad2 Fix dumb test bug
dir_substring is in the test_dir2 folder not the test_dir3 folder
2020-09-03 00:04:27 +01:00
andy.boot
873456eb97 Add Hide hidden flag
From feature request to respect the .gitignore file. Decided to bundle in
respect for hidden files into the same feature [otherwise if you obey
the .gitignore file you still endup showing the .git directory]
2020-09-03 00:04:27 +01:00
andy.boot
4e1180e502 hack 2020-08-30 10:39:03 +01:00
andy.boot
4ea8d9339e Support listing directories by number of files
Based of https://github.com/bootandy/dust/pull/104

Idea is to allow users to find the number of files in each directory
instead of size.
2020-08-30 10:39:03 +01:00
andy.boot
82237c6bde Use full width of terminal (extra 2 characters)
Make output 2 characters wider to use the full width of terminal
2020-08-19 00:30:46 +01:00
andy.boot
109df305a3 Upgrade packages 2020-08-18 23:12:28 +01:00
andy.boot
d03a9796f4 Refactor display code
Pull out parts into separate functions
2020-08-18 23:12:28 +01:00
andy.boot
8e591f3dd5 Move test directories into test dir 2020-08-18 23:12:28 +01:00
andy.boot
086f11988c Remove stray printlns 2020-08-18 23:12:28 +01:00
andy.boot
52eca1ff78 Change bar graphs
Change bars so we always show 'correct' length for current row this
means we ignore the parent's level
2020-08-18 23:12:28 +01:00
andy.boot
e80c60b027 Do not print filenames that are too long
https://github.com/bootandy/dust/issues/92

Not 100% sure if this code is clean yet, may well be a better way to do
it

Also:
Added test directory with incredably long name as test case.

Update test_symlinks.py for mac runners. Mac test runners create files
with very long names, hence the tests fail periodically unless they look
for a truncated name with '..' at the end.
2020-08-16 21:19:15 +01:00
andy.boot
e118814684 Tests: Remove any entries in existing tmp dir
Delete the existing dir before copying the new dir over it incase its
contents have changed.

Delete & Copy directories before tests are run.

On test runners the tests run in parallel so we can't write and clean
up at the start and end of each test. Unless each test copys the data to
a unique dir name. [This may be a better thing to do in the long run]
2020-08-16 21:19:15 +01:00
andy.boot
1546cf2eba Update README.md
Fix link to packages.
2020-08-13 09:36:25 +01:00
andy.boot
4f06de8044 Notes on how to publish
Install new release locally before pushing
Noted here because I forget how to do this
2020-08-02 14:26:33 +01:00
andy.boot
2ca6fcc9ce Increment version 2020-08-02 14:26:33 +01:00
pom421
278a31971c add brew command in README (#97)
* add brew command in README

* Add an emoji

* remove unncessary space
2020-08-02 11:47:12 +01:00
andy.boot
479e5899c6 Merge pull request #96 from yuqio/change-ansi-red
Change red ANSI color code for size of biggest file
2020-06-29 13:21:28 +01:00
yuqio
c4c173e40e Change red ANSI color code for size of biggest file
The enum variant `ansi_term::Colour::Red` (which uses the ANSI color code `31`)
is used instead of the code `196` to display the size of the largest file. This
means that the red color of the terminal theme is used which is probably
preferred by most users.
2020-06-29 01:00:11 +02:00
andy.boot
b8410ee4da Merge pull request #95 from j2ghz/patch-1
Add packaging status
2020-06-07 13:09:23 +01:00
Jozef Hollý
0cb0396a84 Add packaging status 2020-06-07 11:07:04 +02:00
andy.boot
b1fe078e06 Merge pull request #93 from bootandy/ab-fixes
Ab various minor fixes
2020-06-02 21:04:20 +01:00
andy.boot
b3d446bfef Display: No padding if no bars drawn
If not drawing the percent bars do not add padding to the filenames
https://github.com/bootandy/dust/issues/89
2020-06-02 13:57:46 +01:00
andy.boot
34be81c216 Windows: If no color flag is already set
... then do not print a warning message
https://github.com/bootandy/dust/issues/87
2020-06-02 13:45:12 +01:00
andy.boot
342164d357 Tests: Add Missing copy command
should have been added with previous commit, would occastionaly have
caused tests to fail if tests run in random order
2020-03-28 18:32:40 +00:00
andy.boot
bfa3594fe8 Merge pull request #86 from bootandy/ab-run-tests-tmp
Move from ignore to jwalk &  refactor tests
2020-03-28 16:27:45 +00:00
andy.boot
1f120de168 Run tests on /tmp directory 2020-03-28 16:07:35 +00:00
andy.boot
c0048b2ae4 Handle running with bad parameter
Earlier refactor caused running with: 'dust -' to crash
2020-03-28 15:59:07 +00:00
andy.boot
402a8f8249 Remove thread parameter
Code to limit number of threads removed. ignore crate seems quite good
at using CPUs correctly
2020-03-28 15:59:07 +00:00
andy.boot
7cc7047b28 Move from assert_cli to assert_cmd
assert_cli is deprecated.
This allows us to use 'or' in the output of our integration tests
2020-03-28 15:06:40 +00:00
andy.boot
1953e107c2 Move everything to ignore instead of jwalk 2020-03-22 23:58:07 +00:00
andy.boot
f096e82754 Merge pull request #81 from rivy/fix.cicd-features
Maint/CI ~ fix features option for GHA `cargo ...` and `cross ...`
2020-03-15 09:50:37 +00:00
Roy Ivy III
c3415df4b1 Maint/CI ~ fix features option for GHA cargo ... and cross ... 2020-03-10 14:10:28 -05:00
22 changed files with 1237 additions and 1027 deletions

View File

@@ -32,6 +32,12 @@ jobs:
case ${{ matrix.job.os }} in windows-latest) unset JOB_DO_FORMAT_TESTING ;; esac;
echo set-output name=JOB_DO_FORMAT_TESTING::${JOB_DO_FORMAT_TESTING:-<empty>/false}
echo ::set-output name=JOB_DO_FORMAT_TESTING::${JOB_DO_FORMAT_TESTING}
# target-specific options
# * CARGO_FEATURES_OPTION
CARGO_FEATURES_OPTION='' ;
if [ -n "${{ matrix.job.features }}" ]; then CARGO_FEATURES_OPTION='--features "${{ matrix.job.features }}"' ; fi
echo set-output name=CARGO_FEATURES_OPTION::${CARGO_FEATURES_OPTION}
echo ::set-output name=CARGO_FEATURES_OPTION::${CARGO_FEATURES_OPTION}
- name: Install `rust` toolchain
uses: actions-rs/toolchain@v1
with:
@@ -50,7 +56,7 @@ jobs:
uses: actions-rs/cargo@v1
with:
command: clippy
args: ${{ matrix.job.cargo-options }} -- -D warnings
args: ${{ matrix.job.cargo-options }} ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }} -- -D warnings
min_version:
name: MinSRV # Minimum supported rust version
@@ -149,6 +155,11 @@ jobs:
echo set-output name=DEPLOY::${DEPLOY:-<empty>/false}
echo ::set-output name=DEPLOY::${DEPLOY}
# target-specific options
# * CARGO_FEATURES_OPTION
CARGO_FEATURES_OPTION='' ;
if [ -n "${{ matrix.job.features }}" ]; then CARGO_FEATURES_OPTION='--features "${{ matrix.job.features }}"' ; fi
echo set-output name=CARGO_FEATURES_OPTION::${CARGO_FEATURES_OPTION}
echo ::set-output name=CARGO_FEATURES_OPTION::${CARGO_FEATURES_OPTION}
# * CARGO_USE_CROSS (truthy)
CARGO_USE_CROSS='true' ; case '${{ matrix.job.use-cross }}' in ''|0|f|false|n|no) unset CARGO_USE_CROSS ;; esac;
echo set-output name=CARGO_USE_CROSS::${CARGO_USE_CROSS:-<empty>/false}
@@ -193,13 +204,13 @@ jobs:
with:
use-cross: ${{ steps.vars.outputs.CARGO_USE_CROSS }}
command: build
args: --release --target=${{ matrix.job.target }} ${{ matrix.job.cargo-options }}
args: --release --target=${{ matrix.job.target }} ${{ matrix.job.cargo-options }} ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }}
- name: Test
uses: actions-rs/cargo@v1
with:
use-cross: ${{ steps.vars.outputs.CARGO_USE_CROSS }}
command: test
args: --target=${{ matrix.job.target }} ${{ steps.vars.outputs.CARGO_TEST_OPTIONS}} ${{ matrix.job.cargo-options }}
args: --target=${{ matrix.job.target }} ${{ steps.vars.outputs.CARGO_TEST_OPTIONS}} ${{ matrix.job.cargo-options }} ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }}
- name: Archive executable artifacts
uses: actions/upload-artifact@master
with:

668
Cargo.lock generated
View File

@@ -1,607 +1,529 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "aho-corasick"
version = "0.7.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5"
dependencies = [
"memchr",
]
[[package]]
name = "ansi_term"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
dependencies = [
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi",
]
[[package]]
name = "ansi_term"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
dependencies = [
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi",
]
[[package]]
name = "assert_cli"
version = "0.6.3"
name = "assert_cmd"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3dc1679af9a1ab4bea16f228b05d18f8363f8327b1fa8db00d2760cfafc6b61e"
dependencies = [
"colored 1.9.2 (registry+https://github.com/rust-lang/crates.io-index)",
"difference 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"environment 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.47 (registry+https://github.com/rust-lang/crates.io-index)",
"doc-comment",
"predicates",
"predicates-core",
"predicates-tree",
"wait-timeout",
]
[[package]]
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
"hermit-abi 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
"hermit-abi",
"libc",
"winapi",
]
[[package]]
name = "autocfg"
version = "0.1.7"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "backtrace"
version = "0.3.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"backtrace-sys 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "backtrace-sys"
version = "0.1.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
]
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
[[package]]
name = "bitflags"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
[[package]]
name = "c2-chacha"
version = "0.2.3"
name = "bstr"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "473fc6b38233f9af7baa94fb5852dca389e3d95b8e21c8e3719301462c5d9faf"
dependencies = [
"ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
"memchr",
]
[[package]]
name = "cc"
version = "1.0.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "cfg-if"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "clap"
version = "2.33.0"
version = "2.33.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002"
dependencies = [
"ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
"bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
"vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "colored"
version = "1.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "crossbeam"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"crossbeam-channel 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"crossbeam-deque 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
"crossbeam-epoch 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"crossbeam-queue 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"ansi_term 0.11.0",
"atty",
"bitflags",
"strsim",
"textwrap",
"unicode-width",
"vec_map",
]
[[package]]
name = "crossbeam-channel"
version = "0.4.0"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b153fe7cbef478c567df0f972e02e6d736db11affe43dfc9c56a9374d1adfb87"
dependencies = [
"crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "crossbeam-deque"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"crossbeam-epoch 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "crossbeam-epoch"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"memoffset 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
"scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "crossbeam-queue"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"crossbeam-utils 0.7.2",
"maybe-uninit",
]
[[package]]
name = "crossbeam-utils"
version = "0.7.0"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8"
dependencies = [
"autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"autocfg",
"cfg-if 0.1.10",
"lazy_static",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02d96d1e189ef58269ebe5b97953da3274d83a93af647c2ddd6f9dab28cedb8d"
dependencies = [
"autocfg",
"cfg-if 1.0.0",
"lazy_static",
]
[[package]]
name = "difference"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198"
[[package]]
name = "doc-comment"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
[[package]]
name = "du-dust"
version = "0.5.1"
version = "0.5.4"
dependencies = [
"ansi_term 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)",
"assert_cli 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
"clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
"jwalk 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"lscolors 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"num_cpus 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
"tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"terminal_size 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"ansi_term 0.12.1",
"assert_cmd",
"clap",
"crossbeam-channel",
"ignore",
"lscolors",
"num_cpus",
"stfu8",
"tempfile",
"terminal_size",
"thousands",
"unicode-width",
"walkdir",
"winapi-util",
]
[[package]]
name = "either"
version = "1.5.3"
name = "fnv"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "environment"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "failure"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"backtrace 0.3.44 (registry+https://github.com/rust-lang/crates.io-index)",
"failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "failure_derive"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)",
"synstructure 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "getrandom"
version = "0.1.14"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4060f4657be78b8e766215b02b18a2e862d83745545de804638e2b545e81aee6"
dependencies = [
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
"wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 1.0.0",
"libc",
"wasi",
]
[[package]]
name = "globset"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c152169ef1e421390738366d2f796655fec62621dabbd0fd476f905934061e4a"
dependencies = [
"aho-corasick",
"bstr",
"fnv",
"log",
"regex",
]
[[package]]
name = "hermit-abi"
version = "0.1.6"
version = "0.1.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5aca5565f760fb5b220e499d72710ed156fdb74e631659e99377d9ebfbd13ae8"
dependencies = [
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
"libc",
]
[[package]]
name = "itoa"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "jwalk"
version = "0.4.0"
name = "ignore"
version = "0.4.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b287fb45c60bb826a0dc68ff08742b9d88a2fea13d6e0c286b3172065aaf878c"
dependencies = [
"crossbeam 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)",
"rayon 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"crossbeam-utils 0.8.1",
"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.66"
version = "0.2.82"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89203f3fba0a3795506acaad8ebce3c80c0af93f994d5a1d7a0b1eeb23271929"
[[package]]
name = "log"
version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fcf3805d4480bb5b86070dcfeb9e2cb2ebc148adb753c5cca5f884d1d65a42b2"
dependencies = [
"cfg-if 0.1.10",
]
[[package]]
name = "lscolors"
version = "0.6.0"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d24b894c45c9da468621cdd615a5a79ee5e5523dd4f75c76ebc03d458940c16e"
dependencies = [
"ansi_term 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)",
"ansi_term 0.12.1",
]
[[package]]
name = "memoffset"
version = "0.5.3"
name = "maybe-uninit"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00"
[[package]]
name = "memchr"
version = "2.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525"
[[package]]
name = "num_cpus"
version = "1.12.0"
version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3"
dependencies = [
"hermit-abi 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
"hermit-abi",
"libc",
]
[[package]]
name = "ppv-lite86"
version = "0.2.6"
version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
[[package]]
name = "proc-macro2"
version = "1.0.8"
name = "predicates"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73dd9b7b200044694dfede9edf907c1ca19630908443e9447e624993700c6932"
dependencies = [
"unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"difference",
"predicates-core",
]
[[package]]
name = "quote"
version = "1.0.2"
name = "predicates-core"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb3dbeaaf793584e29c58c7e3a82bbb3c7c06b63cea68d13b0e3cddc124104dc"
[[package]]
name = "predicates-tree"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aee95d988ee893cb35c06b148c80ed2cd52c8eea927f50ba7a0be1a786aeab73"
dependencies = [
"proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
"predicates-core",
"treeline",
]
[[package]]
name = "rand"
version = "0.7.3"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18519b42a40024d661e1714153e9ad0c3de27cd495760ceb09710920f1098b1e"
dependencies = [
"getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc",
"rand_chacha",
"rand_core",
"rand_hc",
]
[[package]]
name = "rand_chacha"
version = "0.2.1"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d"
dependencies = [
"c2-chacha 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.5.1"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c026d7df8b298d90ccbbc5190bd04d85e159eaf5576caeacf8741da93ccbd2e5"
dependencies = [
"getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)",
"getrandom",
]
[[package]]
name = "rand_hc"
version = "0.2.0"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73"
dependencies = [
"rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rayon"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"crossbeam-deque 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
"either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
"rayon-core 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rayon-core"
version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"crossbeam-deque 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
"crossbeam-queue 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"num_cpus 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_core",
]
[[package]]
name = "redox_syscall"
version = "0.1.56"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05ec8ca9416c5ea37062b502703cd7fcb207736bc294f6e0cf367ac6fc234570"
dependencies = [
"bitflags",
]
[[package]]
name = "regex"
version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9251239e129e16308e70d853559389de218ac275b515068abc96829d05b948a"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
"thread_local",
]
[[package]]
name = "regex-syntax"
version = "0.6.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5eb417147ba9860a96cfe72a0b93bf88fee1744b5636ec99ab20c1aa9376581"
[[package]]
name = "remove_dir_all"
version = "0.5.2"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
dependencies = [
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi",
]
[[package]]
name = "rustc-demangle"
version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "rustc_version"
version = "0.2.3"
name = "same-file"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
dependencies = [
"semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi-util",
]
[[package]]
name = "ryu"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "scopeguard"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "semver"
version = "0.9.0"
name = "stfu8"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4bf70433e3300a3c395d06606a700cdf4205f4f14dbae2c6833127c6bb22db77"
dependencies = [
"semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "semver-parser"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "serde"
version = "1.0.104"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "serde_json"
version = "1.0.47"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"itoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
"ryu 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static",
"regex",
]
[[package]]
name = "strsim"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "syn"
version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "synstructure"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
[[package]]
name = "tempfile"
version = "3.1.0"
version = "3.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22"
dependencies = [
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)",
"redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)",
"remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 1.0.0",
"libc",
"rand",
"redox_syscall",
"remove_dir_all",
"winapi",
]
[[package]]
name = "terminal_size"
version = "0.1.10"
version = "0.1.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4bd2d183bd3fac5f5fe38ddbeb4dc9aec4a39a9d7d59e7491d900302da01cbe1"
dependencies = [
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
"libc",
"winapi",
]
[[package]]
name = "textwrap"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
dependencies = [
"unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-width",
]
[[package]]
name = "unicode-width"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "unicode-xid"
name = "thousands"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3bf63baf9f5039dadc247375c29eb13706706cfde997d0330d05aa63a77d8820"
[[package]]
name = "thread_local"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb9bc092d0d51e76b2b19d9d85534ffc9ec2db959a2523cdae0697e2972cd447"
dependencies = [
"lazy_static",
]
[[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 = "vec_map"
version = "0.8.1"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
[[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"
version = "0.10.1+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93c6c3420963c5c64bca373b25e77acb562081b9bb4dd5bb864187742186cea9"
[[package]]
name = "winapi"
version = "0.3.8"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"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.3"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
dependencies = [
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi",
]
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[metadata]
"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
"checksum ansi_term 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
"checksum assert_cli 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a29ab7c0ed62970beb0534d637a8688842506d0ff9157de83286dacd065c8149"
"checksum atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
"checksum autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2"
"checksum backtrace 0.3.44 (registry+https://github.com/rust-lang/crates.io-index)" = "e4036b9bf40f3cf16aba72a3d65e8a520fc4bafcdc7079aea8f848c58c5b5536"
"checksum backtrace-sys 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)" = "5d6575f128516de27e3ce99689419835fce9643a9b215a14d2b5b685be018491"
"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
"checksum c2-chacha 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "214238caa1bf3a496ec3392968969cab8549f96ff30652c9e56885329315f6bb"
"checksum cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)" = "95e28fa049fda1c330bcf9d723be7663a899c4679724b34c81e9f5a326aab8cd"
"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
"checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9"
"checksum colored 1.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8815e2ab78f3a59928fc32e141fbeece88320a240e43f47b2fd64ea3a88a5b3d"
"checksum crossbeam 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "69323bff1fb41c635347b8ead484a5ca6c3f11914d784170b158d8449ab07f8e"
"checksum crossbeam-channel 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "acec9a3b0b3559f15aee4f90746c4e5e293b701c0f7d3925d24e01645267b68c"
"checksum crossbeam-deque 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c3aa945d63861bfe624b55d153a39684da1e8c0bc8fba932f7ee3a3c16cea3ca"
"checksum crossbeam-epoch 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5064ebdbf05ce3cb95e45c8b086f72263f4166b29b97f6baff7ef7fe047b55ac"
"checksum crossbeam-queue 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c695eeca1e7173472a32221542ae469b3e9aac3a4fc81f7696bcad82029493db"
"checksum crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ce446db02cdc3165b94ae73111e570793400d0794e46125cc4056c81cbb039f4"
"checksum difference 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198"
"checksum either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3"
"checksum environment 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1f4b14e20978669064c33b4c1e0fb4083412e40fe56cbea2eae80fd7591503ee"
"checksum failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "f8273f13c977665c5db7eb2b99ae520952fe5ac831ae4cd09d80c4c7042b5ed9"
"checksum failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0bc225b78e0391e4b8683440bf2e63c2deeeb2ce5189eab46e2b68c6d3725d08"
"checksum getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb"
"checksum hermit-abi 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "eff2656d88f158ce120947499e971d743c05dbcbed62e5bd2f38f1698bbc3772"
"checksum itoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e"
"checksum jwalk 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2b3dbf0a8f61baee43a2918ff50ac6a2d3b2c105bc08ed53bc298779f1263409"
"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
"checksum libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)" = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558"
"checksum lscolors 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ea3b3414b2d015c4fd689815f2551797f3c2296bb241dd709c7da233ec7cba4b"
"checksum memoffset 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "75189eb85871ea5c2e2c15abbdd541185f63b408415e5051f5cac122d8c774b9"
"checksum num_cpus 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "46203554f085ff89c235cd12f7075f3233af9b11ed7c9e16dfe2560d03313ce6"
"checksum ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b"
"checksum proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3acb317c6ff86a4e579dfa00fc5e6cca91ecbb4e7eb2df0468805b674eb88548"
"checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe"
"checksum rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
"checksum rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853"
"checksum rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
"checksum rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
"checksum rayon 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "db6ce3297f9c85e16621bb8cca38a06779ffc31bb8184e1be4bed2be4678a098"
"checksum rayon-core 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "08a89b46efaf957e52b18062fb2f4660f8b8a4dde1807ca002690868ef2c85a9"
"checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84"
"checksum remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e"
"checksum rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783"
"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
"checksum ryu 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bfa8506c1de11c9c4e4c38863ccbe02a305c8188e85a05a784c9e11e1c3910c8"
"checksum scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b42e15e59b18a828bbf5c58ea01debb36b9b096346de35d941dcb89009f24a0d"
"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
"checksum serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)" = "414115f25f818d7dfccec8ee535d76949ae78584fc4f79a6f45a904bf8ab4449"
"checksum serde_json 1.0.47 (registry+https://github.com/rust-lang/crates.io-index)" = "15913895b61e0be854afd32fd4163fcd2a3df34142cf2cb961b310ce694cbf90"
"checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
"checksum syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)" = "af6f3550d8dff9ef7dc34d384ac6f107e5d31c8f57d9f28e0081503f547ac8f5"
"checksum synstructure 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "67656ea1dc1b41b1451851562ea232ec2e5a80242139f7e679ceccfb5d61f545"
"checksum tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9"
"checksum terminal_size 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "e25a60e3024df9029a414be05f46318a77c22538861a22170077d0388c0e926e"
"checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
"checksum unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479"
"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
"checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a"
"checksum wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)" = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
"checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6"
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
"checksum winapi-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4ccfbf554c6ad11084fb7517daca16cfdcaccbdadba4fc336f032a8b12c2ad80"
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"

View File

@@ -1,7 +1,7 @@
[package]
name = "du-dust"
description = "A more intuitive version of du"
version = "0.5.1"
version = "0.5.4"
authors = ["bootandy <bootandy@gmail.com>", "nebkor <code@ardent.nebcorp.com>"]
edition = "2018"
@@ -21,19 +21,23 @@ name = "dust"
path = "src/main.rs"
[dependencies]
ansi_term = "=0.12"
ansi_term = "0.12"
clap = "=2.33"
jwalk = "0.4.0"
lscolors = "0.6.0"
num_cpus = "1.12"
terminal_size = "0.1.10"
unicode-width = "0.1.7"
lscolors = "0.7"
num_cpus = "1"
terminal_size = "0.1"
unicode-width = "0.1"
ignore="0.4"
crossbeam-channel = "0.4"
walkdir="2.3"
thousands = "0.2"
stfu8 = "0.2"
[target.'cfg(windows)'.dependencies]
winapi-util = "0.1"
[dev-dependencies]
assert_cli = "=0.6"
assert_cmd ="1"
tempfile = "=3"

View File

@@ -14,10 +14,18 @@ Because I want an easy way to see where my disk is being used.
## Install
#### Cargo
#### Cargo <a href="https://repology.org/project/du-dust/versions"><img src="https://repology.org/badge/vertical-allrepos/du-dust.svg" alt="Packaging status" align="right"></a>
* `cargo install du-dust`
#### 🍺 Homebrew (Mac OS)
* `brew install dust`
#### 🍺 Homebrew (Linux)
* `brew tap tgotwig/linux-dust && brew install dust`
#### Download
* Download Linux/Mac binary from [Releases](https://github.com/bootandy/dust/releases)

View File

@@ -1,6 +1,10 @@
# ----------- To do a release ---------
# tag a commit and push:
# edit version in cargo.toml
# tag a commit and push (increment version first):
# git tag v0.4.5
# git push origin v0.4.5
# cargo publish to put it in crates.io
# cargo publish to put it in crates.io
# To install locally [Do before pushing it]
#cargo install --path .

View File

@@ -1,60 +1,46 @@
extern crate ansi_term;
use crate::utils::Node;
use crate::utils::{Errors, Node};
use self::ansi_term::Colour::Fixed;
use self::ansi_term::Colour::Red;
use lscolors::{LsColors, Style};
use terminal_size::{terminal_size, Height, Width};
use unicode_width::UnicodeWidthStr;
use stfu8::encode_u8;
use std::cmp::max;
use std::cmp::min;
use std::fs;
use std::iter::repeat;
use std::path::Path;
use thousands::Separable;
static UNITS: [char; 4] = ['T', 'G', 'M', 'K'];
static BLOCKS: [char; 5] = ['█', '▓', '▒', '░', ' '];
static DEFAULT_TERMINAL_WIDTH: u16 = 80;
pub struct DisplayData {
pub short_paths: bool,
pub is_reversed: bool,
pub colors_on: bool,
pub by_filecount: bool,
pub num_chars_needed_on_left_most: usize,
pub base_size: u64,
pub longest_string_length: usize,
pub ls_colors: LsColors,
}
impl DisplayData {
#[allow(clippy::collapsible_if)]
fn get_tree_chars(&self, was_i_last: bool, has_children: bool) -> &'static str {
if self.is_reversed {
if was_i_last {
if has_children {
"─┴"
} else {
"┌──"
}
} else if has_children {
"├─"
} else {
"├──"
}
} else {
if was_i_last {
if has_children {
"└─┬"
} else {
"└──"
}
} else if has_children {
"├─┬"
} else {
"├──"
}
match (self.is_reversed, was_i_last, has_children) {
(true, true, true) => "┌─┴",
(true, true, false) => "┌──",
(true, false, true) => "─┴",
(true, false, false) => "├──",
(false, true, true) => "└─┬",
(false, true, false) => "└──",
(false, false, true) => "├─┬",
(false, false, false) => "├─",
}
}
@@ -84,15 +70,6 @@ impl DisplayData {
}
}
fn get_children_from_node(node: Node, is_reversed: bool) -> impl Iterator<Item = Node> {
if is_reversed {
let n: Vec<Node> = node.children.into_iter().rev().map(|a| a).collect();
n.into_iter()
} else {
node.children.into_iter()
}
}
struct DrawData<'a> {
indent: String,
percent_bar: String,
@@ -105,13 +82,14 @@ impl DrawData<'_> {
self.indent.to_string() + chars
}
// TODO: can we test this?
fn generate_bar(&self, node: &Node, level: usize) -> String {
let chars_in_bar = self.percent_bar.chars().count();
let num_bars = chars_in_bar as f32 * self.display_data.percent_size(node);
let mut num_not_my_bar = (chars_in_bar as i32) - num_bars as i32;
let mut new_bar = "".to_string();
let idx = 5 - min(5, max(1, level));
let idx = 5 - min(4, max(1, level));
for c in self.percent_bar.chars() {
num_not_my_bar -= 1;
@@ -127,32 +105,37 @@ impl DrawData<'_> {
}
}
fn get_width_of_terminal() -> u16 {
// Windows CI runners detect a very low terminal width
if let Some((Width(w), Height(_h))) = terminal_size() {
max(w, DEFAULT_TERMINAL_WIDTH)
} else {
DEFAULT_TERMINAL_WIDTH
}
}
#[allow(clippy::too_many_arguments)]
pub fn draw_it(
permissions: bool,
errors: Errors,
use_full_path: bool,
is_reversed: bool,
no_colors: bool,
no_percents: bool,
terminal_width: usize,
by_filecount: bool,
root_node: Node,
) {
if !permissions {
if errors.permissions {
eprintln!("Did not have permissions for all directories");
}
if errors.not_found {
eprintln!("Not all directories were found");
}
let num_chars_needed_on_left_most = if by_filecount {
let max_size = root_node.children.iter().map(|n| n.size).fold(0, max);
max_size.separate_with_commas().chars().count()
} else {
5 // Under normal usage we need 5 chars to display the size of a directory
};
let terminal_width = terminal_width - 9 - num_chars_needed_on_left_most;
let num_indent_chars = 3;
let longest_string_length = root_node
.children
.iter()
.map(|c| find_longest_dir_name(&c, " ", !use_full_path))
.map(|c| find_longest_dir_name(&c, num_indent_chars, terminal_width, !use_full_path))
.fold(0, max);
let terminal_width = get_width_of_terminal() - 16;
let max_bar_length = if no_percents || longest_string_length >= terminal_width as usize {
0
@@ -160,50 +143,51 @@ pub fn draw_it(
terminal_width as usize - longest_string_length
};
// handle usize error also add do not show fancy output option
let bar_text = repeat(BLOCKS[0]).take(max_bar_length).collect::<String>();
let first_size_bar = repeat(BLOCKS[0]).take(max_bar_length).collect::<String>();
for c in get_children_from_node(root_node, is_reversed) {
for c in root_node.get_children_from_node(is_reversed) {
let display_data = DisplayData {
short_paths: !use_full_path,
is_reversed,
colors_on: !no_colors,
by_filecount,
num_chars_needed_on_left_most,
base_size: c.size,
longest_string_length,
ls_colors: LsColors::from_env().unwrap_or_default(),
};
let draw_data = DrawData {
indent: "".to_string(),
percent_bar: bar_text.clone(),
percent_bar: first_size_bar.clone(),
display_data: &display_data,
};
display_node(c, &draw_data, true, true);
}
}
// We can probably pass depth instead of indent here.
// It is ugly to feed in ' ' instead of the actual tree characters but we don't need them yet.
fn find_longest_dir_name(node: &Node, indent: &str, long_paths: bool) -> usize {
fn find_longest_dir_name(node: &Node, indent: usize, terminal: usize, long_paths: bool) -> usize {
let printable_name = get_printable_name(&node.name, long_paths);
let longest = get_unicode_width_of_indent_and_name(indent, &printable_name);
let longest = min(
UnicodeWidthStr::width(&*printable_name) + 1 + indent,
terminal,
);
// each none root tree drawing is 2 chars
let full_indent: String = indent.to_string() + " ";
// each none root tree drawing is 2 more chars, hence we increment indent by 2
node.children
.iter()
.map(|c| find_longest_dir_name(c, &*full_indent, long_paths))
.map(|c| find_longest_dir_name(c, indent + 2, terminal, long_paths))
.fold(longest, max)
}
fn display_node(node: Node, draw_data: &DrawData, is_biggest: bool, is_last: bool) {
let indent2 = draw_data.get_new_indent(!node.children.is_empty(), is_last);
// hacky way of working out how deep we are in the tree
let level = ((indent2.chars().count() - 1) / 2) - 1;
let indent = draw_data.get_new_indent(!node.children.is_empty(), is_last);
let level = ((indent.chars().count() - 1) / 2) - 1;
let bar_text = draw_data.generate_bar(&node, level);
let to_print = format_string(
&node,
&*indent2,
&*indent,
&*bar_text,
is_biggest,
draw_data.display_data,
@@ -214,13 +198,17 @@ fn display_node(node: Node, draw_data: &DrawData, is_biggest: bool, is_last: boo
}
let dd = DrawData {
indent: clean_indentation_string(&*indent2),
indent: clean_indentation_string(&*indent),
percent_bar: bar_text,
display_data: draw_data.display_data,
};
let num_siblings = node.children.len() as u64;
for (count, c) in get_children_from_node(node, draw_data.display_data.is_reversed).enumerate() {
let num_siblings = node.num_siblings();
for (count, c) in node
.get_children_from_node(draw_data.display_data.is_reversed)
.enumerate()
{
let is_biggest = dd.display_data.is_biggest(count, num_siblings);
let was_i_last = dd.display_data.is_last(count, num_siblings);
display_node(c, &dd, is_biggest, was_i_last);
@@ -263,12 +251,33 @@ fn get_printable_name<P: AsRef<Path>>(dir_name: &P, long_paths: bool) -> String
dir_name
}
};
printable_name.display().to_string()
encode_u8(printable_name.display().to_string().as_bytes())
}
fn get_unicode_width_of_indent_and_name(indent: &str, name: &str) -> usize {
fn pad_or_trim_filename(node: &Node, indent: &str, display_data: &DisplayData) -> String {
let name = get_printable_name(&node.name, display_data.short_paths);
let indent_and_name = format!("{} {}", indent, name);
UnicodeWidthStr::width(&*indent_and_name)
let width = UnicodeWidthStr::width(&*indent_and_name);
// Add spaces after the filename so we can draw the % used bar chart.
let name_and_padding = name
+ &(repeat(" ")
.take(display_data.longest_string_length - width)
.collect::<String>());
maybe_trim_filename(name_and_padding, display_data)
}
fn maybe_trim_filename(name_in: String, display_data: &DisplayData) -> String {
if UnicodeWidthStr::width(&*name_in) > display_data.longest_string_length {
let name = name_in
.chars()
.take(display_data.longest_string_length - 2)
.collect::<String>();
name + ".."
} else {
name_in
}
}
pub fn format_string(
@@ -278,31 +287,49 @@ pub fn format_string(
is_biggest: bool,
display_data: &DisplayData,
) -> String {
let pretty_size = format!("{:>5}", human_readable_number(node.size));
let (percents, name_and_padding) = get_name_percent(node, indent, percent_bar, display_data);
let pretty_size = get_pretty_size(node, is_biggest, display_data);
let pretty_name = get_pretty_name(node, name_and_padding, display_data);
format!("{} {} {}{}", pretty_size, indent, pretty_name, percents)
}
let percent_size_str = format!("{:.0}%", display_data.percent_size(node) * 100.0);
let name = get_printable_name(&node.name, display_data.short_paths);
let width = get_unicode_width_of_indent_and_name(indent, &name);
let name_and_padding = name
+ &(repeat(" ")
.take(display_data.longest_string_length - width)
.collect::<String>());
let percents = if percent_bar != "" {
format!("{}{:>4}", percent_bar, percent_size_str)
fn get_name_percent(
node: &Node,
indent: &str,
bar_chart: &str,
display_data: &DisplayData,
) -> (String, String) {
if !bar_chart.is_empty() {
let percent_size_str = format!("{:.0}%", display_data.percent_size(node) * 100.0);
let percents = format!("{}{:>4}", bar_chart, percent_size_str);
let name_and_padding = pad_or_trim_filename(node, indent, display_data);
(percents, name_and_padding)
} else {
"".into()
let n = get_printable_name(&node.name, display_data.short_paths);
let name = maybe_trim_filename(n, display_data);
("".into(), name)
}
}
fn get_pretty_size(node: &Node, is_biggest: bool, display_data: &DisplayData) -> String {
let output = if display_data.by_filecount {
let size_as_str = node.size.separate_with_commas();
let spaces_to_add =
display_data.num_chars_needed_on_left_most - size_as_str.chars().count();
size_as_str + &*repeat(' ').take(spaces_to_add).collect::<String>()
} else {
format!("{:>5}", human_readable_number(node.size))
};
let pretty_size = if is_biggest && display_data.colors_on {
format!("{}", Fixed(196).paint(pretty_size))
if is_biggest && display_data.colors_on {
format!("{}", Red.paint(output))
} else {
pretty_size
};
output
}
}
let pretty_name = if display_data.colors_on {
fn get_pretty_name(node: &Node, name_and_padding: String, display_data: &DisplayData) -> String {
if display_data.colors_on {
let meta_result = fs::metadata(node.name.clone());
let directory_color = display_data
.ls_colors
@@ -313,9 +340,7 @@ pub fn format_string(
format!("{}", ansi_style.paint(name_and_padding))
} else {
name_and_padding
};
format!("{} {} {}{}", pretty_size, indent, pretty_name, percents)
}
}
fn human_readable_number(size: u64) -> String {
@@ -335,6 +360,63 @@ fn human_readable_number(size: u64) -> String {
mod tests {
#[allow(unused_imports)]
use super::*;
#[allow(unused_imports)]
use std::path::PathBuf;
#[cfg(test)]
fn get_fake_display_data(longest_string_length: usize) -> DisplayData {
DisplayData {
short_paths: true,
is_reversed: false,
colors_on: false,
by_filecount: false,
num_chars_needed_on_left_most: 5,
base_size: 1,
longest_string_length,
ls_colors: LsColors::from_env().unwrap_or_default(),
}
}
#[test]
fn test_format_str() {
let n = Node {
name: PathBuf::from("/short"),
size: 2_u64.pow(12), // This is 4.0K
children: vec![],
};
let indent = "┌─┴";
let percent_bar = "";
let is_biggest = false;
let s = format_string(
&n,
indent,
percent_bar,
is_biggest,
&get_fake_display_data(6),
);
assert_eq!(s, " 4.0K ┌─┴ short");
}
#[test]
fn test_format_str_long_name() {
let name = "very_long_name_longer_than_the_eighty_character_limit_very_long_name_this_bit_will_truncate";
let n = Node {
name: PathBuf::from(name),
size: 2_u64.pow(12), // This is 4.0K
children: vec![],
};
let indent = "┌─┴";
let percent_bar = "";
let is_biggest = false;
let dd = get_fake_display_data(64);
let s = format_string(&n, indent, percent_bar, is_biggest, &dd);
assert_eq!(
s,
" 4.0K ┌─┴ very_long_name_longer_than_the_eighty_character_limit_very_lon.."
);
}
#[test]
fn test_human_readable_number() {

View File

@@ -1,6 +1,9 @@
#[macro_use]
extern crate clap;
extern crate crossbeam_channel as channel;
extern crate ignore;
extern crate unicode_width;
extern crate walkdir;
use self::display::draw_it;
use crate::utils::is_a_parent_of;
@@ -8,24 +11,30 @@ use clap::{App, AppSettings, Arg};
use std::cmp::max;
use std::path::PathBuf;
use terminal_size::{terminal_size, Height, Width};
use utils::{find_big_ones, get_dir_tree, simplify_dir_names, sort, trim_deep_ones, Node};
use utils::{find_big_ones, get_dir_tree, simplify_dir_names, sort, Node};
mod display;
mod utils;
static DEFAULT_NUMBER_OF_LINES: usize = 30;
static DEFAULT_TERMINAL_WIDTH: usize = 80;
#[cfg(windows)]
fn init_color(no_color: bool) -> bool {
// Required for windows 10
// Fails to resolve for windows 8 so disable color
match ansi_term::enable_ansi_support() {
Ok(_) => no_color,
Err(_) => {
eprintln!(
"This version of Windows does not support ANSI colors, setting no_color flag"
);
true
// If no color is already set do not print a warning message
if no_color {
true
} else {
// Required for windows 10
// Fails to resolve for windows 8 so disable color
match ansi_term::enable_ansi_support() {
Ok(_) => no_color,
Err(_) => {
eprintln!(
"This version of Windows does not support ANSI colors, setting no_color flag"
);
true
}
}
}
}
@@ -44,6 +53,15 @@ fn get_height_of_terminal() -> usize {
}
}
fn get_width_of_terminal() -> usize {
// Windows CI runners detect a very low terminal width
if let Some((Width(w), Height(_h))) = terminal_size() {
max(w as usize, DEFAULT_TERMINAL_WIDTH)
} else {
DEFAULT_TERMINAL_WIDTH
}
}
fn main() {
let default_height = get_height_of_terminal();
let def_num_str = default_height.to_string();
@@ -59,18 +77,11 @@ fn main() {
.help("Depth to show")
.takes_value(true),
)
.arg(
Arg::with_name("threads")
.short("t")
.long("threads")
.help("Number of threads to spawn simultaneously")
.takes_value(true),
)
.arg(
Arg::with_name("number_of_lines")
.short("n")
.long("number-of-lines")
.help("Number of lines of output to show")
.help("Number of lines of output to show. This is Height, (but h is help)")
.takes_value(true)
.default_value(def_num_str.as_ref()),
)
@@ -119,6 +130,27 @@ fn main() {
.long("no-percent-bars")
.help("No percent bars or percentages will be displayed"),
)
.arg(
Arg::with_name("by_filecount")
.short("f")
.long("filecount")
.help("Directory 'size' is number of child files/dirs not disk size"),
)
.arg(
Arg::with_name("ignore_hidden")
.short("i") // Do not use 'h' this is used by 'help'
.long("ignore_hidden")
.help("Obey .git_ignore rules & Do not display hidden files"),
)
.arg(
Arg::with_name("width")
.short("w")
.long("terminal_width")
.takes_value(true)
.number_of_values(1)
.help("Specify width of output overriding the auto detection of terminal width"),
)
.arg(Arg::with_name("inputs").multiple(true))
.get_matches();
@@ -137,27 +169,14 @@ fn main() {
}
};
let temp_threads = options.value_of("threads").and_then(|threads| {
threads
.parse::<usize>()
.map_err(|_| eprintln!("Ignoring bad value for threads: {:?}", threads))
.ok()
});
// Bug in JWalk
// https://github.com/jessegrosjean/jwalk/issues/15
// We force it to use 2 threads if there is only 1 cpu
// as JWalk breaks if it tries to run on a single cpu
let threads = {
if temp_threads.is_none() && num_cpus::get() == 1 {
Some(2)
} else {
temp_threads
}
let terminal_width = match value_t!(options.value_of("width"), usize) {
Ok(v) => v,
Err(_) => get_width_of_terminal(),
};
let depth = options.value_of("depth").and_then(|depth| {
depth
.parse::<u64>()
.parse::<usize>()
.map(|v| v + 1)
.map_err(|_| eprintln!("Ignoring bad value for depth"))
.ok()
@@ -174,35 +193,40 @@ fn main() {
Some(i) => Some(i.map(PathBuf::from).collect()),
None => None,
};
let by_filecount = options.is_present("by_filecount");
let show_hidden = !options.is_present("ignore_hidden");
let simplified_dirs = simplify_dir_names(target_dirs);
let (permissions, nodes) = get_dir_tree(
let (errors, nodes) = get_dir_tree(
&simplified_dirs,
&ignore_directories,
use_apparent_size,
limit_filesystem,
threads,
by_filecount,
show_hidden,
);
let sorted_data = sort(nodes);
let biggest_ones = {
match depth {
None => find_big_ones(sorted_data, number_of_lines + simplified_dirs.len()),
Some(d) => trim_deep_ones(sorted_data, d, &simplified_dirs),
None => find_big_ones(sorted_data, number_of_lines),
Some(_) => sorted_data,
}
};
let tree = build_tree(biggest_ones, depth);
draw_it(
permissions,
errors,
options.is_present("display_full_paths"),
!options.is_present("reverse"),
no_colors,
options.is_present("no_bars"),
terminal_width,
by_filecount,
tree,
);
}
fn build_tree(biggest_ones: Vec<(PathBuf, u64)>, depth: Option<u64>) -> Node {
fn build_tree(biggest_ones: Vec<(PathBuf, u64)>, depth: Option<usize>) -> Node {
let mut top_parent = Node::default();
// assume sorted order
@@ -217,7 +241,7 @@ fn build_tree(biggest_ones: Vec<(PathBuf, u64)>, depth: Option<u64>) -> Node {
top_parent
}
fn recursively_build_tree(parent_node: &mut Node, new_node: Node, depth: Option<u64>) {
fn recursively_build_tree(parent_node: &mut Node, new_node: Node, depth: Option<usize>) {
let new_depth = match depth {
None => None,
Some(0) => return,

View File

@@ -1,15 +1,22 @@
use jwalk::DirEntry;
use std::cmp::Ordering;
use std::collections::HashMap;
use std::collections::HashSet;
use std::path::{Path, PathBuf};
use std::sync::atomic::AtomicBool;
use jwalk::WalkDir;
use channel::Receiver;
use std::thread::JoinHandle;
use ignore::{WalkBuilder, WalkState};
use std::sync::atomic;
use std::thread;
mod platform;
use self::platform::*;
#[derive(Debug, Default, Eq)]
type PathData = (PathBuf, u64, Option<(u64, u64)>);
#[derive(Debug, Default, Eq, Clone)]
pub struct Node {
pub name: PathBuf,
pub size: u64,
@@ -38,6 +45,26 @@ impl PartialEq for Node {
}
}
impl Node {
pub fn num_siblings(&self) -> u64 {
self.children.len() as u64
}
pub fn get_children_from_node(&self, is_reversed: bool) -> impl Iterator<Item = Node> {
if is_reversed {
let children: Vec<Node> = self.children.clone().into_iter().rev().collect();
children.into_iter()
} else {
self.children.clone().into_iter()
}
}
}
pub struct Errors {
pub permissions: bool,
pub not_found: bool,
}
pub fn is_a_parent_of<P: AsRef<Path>>(parent: P, child: P) -> bool {
let parent = parent.as_ref();
let child = child.as_ref();
@@ -70,46 +97,147 @@ pub fn simplify_dir_names<P: AsRef<Path>>(filenames: Vec<P>) -> HashSet<PathBuf>
top_level_names
}
fn prepare_walk_dir_builder<P: AsRef<Path>>(
top_level_names: &HashSet<P>,
limit_filesystem: bool,
show_hidden: bool,
) -> WalkBuilder {
let mut it = top_level_names.iter();
let mut builder = WalkBuilder::new(it.next().unwrap());
builder.follow_links(false);
if show_hidden {
builder.hidden(false);
builder.ignore(false);
builder.git_global(false);
builder.git_ignore(false);
builder.git_exclude(false);
}
if limit_filesystem {
builder.same_file_system(true);
}
for b in it {
builder.add(b);
}
builder
}
fn is_not_found(e: &ignore::Error) -> bool {
use ignore::Error;
if let Error::WithPath { err, .. } = e {
if let Error::Io(e) = &**err {
if e.kind() == std::io::ErrorKind::NotFound {
return true;
}
}
}
false
}
pub fn get_dir_tree<P: AsRef<Path>>(
top_level_names: &HashSet<P>,
ignore_directories: &Option<Vec<PathBuf>>,
apparent_size: bool,
limit_filesystem: bool,
threads: Option<usize>,
) -> (bool, HashMap<PathBuf, u64>) {
let mut permissions = 0;
let mut data: HashMap<PathBuf, u64> = HashMap::new();
let restricted_filesystems = if limit_filesystem {
get_allowed_filesystems(top_level_names)
} else {
None
};
by_filecount: bool,
show_hidden: bool,
) -> (Errors, HashMap<PathBuf, u64>) {
let (tx, rx) = channel::bounded::<PathData>(1000);
let mut examine_dir_args = ExamineDirMutArsg {
data: &mut data,
file_count_no_permission: &mut permissions,
let permissions_flag = AtomicBool::new(false);
let not_found_flag = AtomicBool::new(false);
let t2 = top_level_names
.iter()
.map(|p| p.as_ref().to_path_buf())
.collect();
let t = create_reader_thread(rx, t2, apparent_size);
let walk_dir_builder = prepare_walk_dir_builder(top_level_names, limit_filesystem, show_hidden);
walk_dir_builder.build_parallel().run(|| {
let txc = tx.clone();
let pf = &permissions_flag;
let nf = &not_found_flag;
Box::new(move |path| {
match path {
Ok(p) => {
if let Some(dirs) = ignore_directories {
let path = p.path();
let parts = path.components().collect::<Vec<std::path::Component>>();
for d in dirs {
if parts
.windows(d.components().count())
.any(|window| window.iter().collect::<PathBuf>() == *d)
{
return WalkState::Continue;
}
}
}
let maybe_size_and_inode = get_metadata(&p, apparent_size);
match maybe_size_and_inode {
Some(data) => {
let (size, inode_device) =
if by_filecount { (1, data.1) } else { data };
txc.send((p.into_path(), size, inode_device)).unwrap();
}
None => {
pf.store(true, atomic::Ordering::Relaxed);
}
}
}
Err(e) => {
if is_not_found(&e) {
nf.store(true, atomic::Ordering::Relaxed);
} else {
pf.store(true, atomic::Ordering::Relaxed);
}
}
};
WalkState::Continue
})
});
drop(tx);
let data = t.join().unwrap();
let errors = Errors {
permissions: permissions_flag.load(atomic::Ordering::SeqCst),
not_found: not_found_flag.load(atomic::Ordering::SeqCst),
};
for b in top_level_names.iter() {
examine_dir(
b,
apparent_size,
&restricted_filesystems,
ignore_directories,
threads,
&mut examine_dir_args,
);
}
(permissions == 0, data)
(errors, data)
}
fn get_allowed_filesystems<P: AsRef<Path>>(top_level_names: &HashSet<P>) -> Option<HashSet<u64>> {
let mut limit_filesystems: HashSet<u64> = HashSet::new();
for file_name in top_level_names.iter() {
if let Ok(a) = get_filesystem(file_name) {
limit_filesystems.insert(a);
fn create_reader_thread(
rx: Receiver<PathData>,
top_level_names: HashSet<PathBuf>,
apparent_size: bool,
) -> JoinHandle<HashMap<PathBuf, u64>> {
// Receiver thread
thread::spawn(move || {
let mut hash: HashMap<PathBuf, u64> = HashMap::new();
let mut inodes: HashSet<(u64, u64)> = HashSet::new();
for dent in rx {
let (path, size, maybe_inode_device) = dent;
if should_ignore_file(apparent_size, &mut inodes, maybe_inode_device) {
continue;
} else {
for p in path.ancestors() {
let s = hash.entry(p.to_path_buf()).or_insert(0);
*s += size;
if top_level_names.contains(p) {
break;
}
}
}
}
}
Some(limit_filesystems)
hash
})
}
pub fn normalize_path<P: AsRef<Path>>(path: P) -> PathBuf {
@@ -122,64 +250,8 @@ pub fn normalize_path<P: AsRef<Path>>(path: P) -> PathBuf {
path.as_ref().components().collect::<PathBuf>()
}
struct ExamineDirMutArsg<'a> {
data: &'a mut HashMap<PathBuf, u64>,
file_count_no_permission: &'a mut u64,
}
fn examine_dir<P: AsRef<Path>>(
top_dir: P,
apparent_size: bool,
filesystems: &Option<HashSet<u64>>,
ignore_directories: &Option<Vec<PathBuf>>,
threads: Option<usize>,
mut_args: &mut ExamineDirMutArsg,
) {
let top_dir = top_dir.as_ref();
let mut inodes: HashSet<(u64, u64)> = HashSet::new();
let mut iter = WalkDir::new(top_dir)
.preload_metadata(true)
.skip_hidden(false);
if let Some(threads_to_start) = threads {
iter = iter.num_threads(threads_to_start);
}
'entry: for entry in iter {
if let Ok(e) = entry {
let maybe_size_and_inode = get_metadata(&e, apparent_size);
if let Some(dirs) = ignore_directories {
let path = e.path();
let parts = path.components().collect::<Vec<std::path::Component>>();
for d in dirs {
let seq = d.components().collect::<Vec<std::path::Component>>();
if parts
.windows(seq.len())
.any(|window| window.iter().collect::<PathBuf>() == *d)
{
continue 'entry;
}
}
}
match maybe_size_and_inode {
Some(data) => {
let (size, inode_device) = data;
if !should_ignore_file(apparent_size, filesystems, &mut inodes, inode_device) {
process_file_with_size_and_inode(top_dir, mut_args.data, e, size)
}
}
None => *mut_args.file_count_no_permission += 1,
}
} else {
*mut_args.file_count_no_permission += 1
}
}
}
fn should_ignore_file(
apparent_size: bool,
restricted_filesystems: &Option<HashSet<u64>>,
inodes: &mut HashSet<(u64, u64)>,
maybe_inode_device: Option<(u64, u64)>,
) -> bool {
@@ -187,13 +259,6 @@ fn should_ignore_file(
None => false,
Some(data) => {
let (inode, device) = data;
// Ignore files on different devices (if flag applied)
if let Some(rs) = restricted_filesystems {
if !rs.contains(&device) {
return true;
}
}
if !apparent_size {
// Ignore files already visited or symlinked
if inodes.contains(&(inode, device)) {
@@ -206,28 +271,6 @@ fn should_ignore_file(
}
}
fn process_file_with_size_and_inode<P: AsRef<Path>>(
top_dir: P,
data: &mut HashMap<PathBuf, u64>,
e: DirEntry,
size: u64,
) {
let top_dir = top_dir.as_ref();
// This path and all its parent paths have their counter incremented
for path in e.path().ancestors() {
// This is required due to bug in Jwalk that adds '/' to all sub dir lists
// see: https://github.com/jessegrosjean/jwalk/issues/13
if path.to_string_lossy() == "/" && top_dir.to_string_lossy() != "/" {
continue;
}
let s = data.entry(normalize_path(path)).or_insert(0);
*s += size;
if path.starts_with(top_dir) && top_dir.starts_with(path) {
break;
}
}
}
pub fn sort_by_size_first_name_second(a: &(PathBuf, u64), b: &(PathBuf, u64)) -> Ordering {
let result = b.1.cmp(&a.1);
if result == Ordering::Equal {
@@ -251,36 +294,6 @@ pub fn find_big_ones(new_l: Vec<(PathBuf, u64)>, max_to_show: usize) -> Vec<(Pat
}
}
fn depth_of_path(name: &PathBuf) -> usize {
// Filter required as paths can have some odd preliminary
// ("Prefix") bits (for example, from windows, "\\?\" or "\\UNC\")
name.components()
.filter(|&c| match c {
std::path::Component::Prefix(_) => false,
_ => true,
})
.count()
}
pub fn trim_deep_ones(
input: Vec<(PathBuf, u64)>,
max_depth: u64,
top_level_names: &HashSet<PathBuf>,
) -> Vec<(PathBuf, u64)> {
let mut result: Vec<(PathBuf, u64)> = Vec::with_capacity(input.len() * top_level_names.len());
for name in top_level_names {
let my_max_depth = depth_of_path(name) + max_depth as usize;
for &(ref k, ref v) in input.iter() {
if k.starts_with(name) && depth_of_path(k) <= my_max_depth {
result.push((k.clone(), *v));
}
}
}
result
}
mod tests {
#[allow(unused_imports)]
use super::*;
@@ -367,19 +380,14 @@ mod tests {
let mut files = HashSet::new();
files.insert((10, 20));
assert!(!should_ignore_file(true, &None, &mut files, Some((0, 0))));
assert!(!should_ignore_file(true, &mut files, Some((0, 0))));
// New file is not known it will be inserted to the hashmp and should not be ignored
assert!(!should_ignore_file(
false,
&None,
&mut files,
Some((11, 12))
));
assert!(!should_ignore_file(false, &mut files, Some((11, 12))));
assert!(files.contains(&(11, 12)));
// The same file will be ignored the second time
assert!(should_ignore_file(false, &None, &mut files, Some((11, 12))));
assert!(should_ignore_file(false, &mut files, Some((11, 12))));
}
#[test]
@@ -387,17 +395,8 @@ mod tests {
let mut files = HashSet::new();
files.insert((10, 20));
let mut devices = HashSet::new();
devices.insert(99);
let od = Some(devices);
// If we are looking at a different device (disk) and the device flag is set
// then apparent_size is irrelevant - we ignore files on other devices
assert!(should_ignore_file(false, &od, &mut files, Some((11, 12))));
assert!(should_ignore_file(true, &od, &mut files, Some((11, 12))));
// We do not ignore files on the same device
assert!(!should_ignore_file(false, &od, &mut files, Some((2, 99))));
assert!(!should_ignore_file(true, &od, &mut files, Some((2, 99))));
assert!(!should_ignore_file(false, &mut files, Some((2, 99))));
assert!(!should_ignore_file(true, &mut files, Some((2, 99))));
}
}

View File

@@ -1,8 +1,6 @@
use jwalk::DirEntry;
use ignore::DirEntry;
#[allow(unused_imports)]
use std::fs;
use std::io;
use std::path::Path;
#[cfg(target_family = "unix")]
fn get_block_size() -> u64 {
@@ -14,13 +12,16 @@ fn get_block_size() -> u64 {
#[cfg(target_family = "unix")]
pub fn get_metadata(d: &DirEntry, use_apparent_size: bool) -> Option<(u64, Option<(u64, u64)>)> {
use std::os::unix::fs::MetadataExt;
d.metadata.as_ref().unwrap().as_ref().ok().map(|md| {
if use_apparent_size {
(md.len(), Some((md.ino(), md.dev())))
} else {
(md.blocks() * get_block_size(), Some((md.ino(), md.dev())))
match d.metadata() {
Ok(md) => {
if use_apparent_size {
Some((md.len(), Some((md.ino(), md.dev()))))
} else {
Some((md.blocks() * get_block_size(), Some((md.ino(), md.dev()))))
}
}
})
Err(_e) => None,
}
}
#[cfg(target_family = "windows")]
@@ -61,6 +62,8 @@ pub fn get_metadata(d: &DirEntry, _use_apparent_size: bool) -> Option<(u64, Opti
// Consistently opening the file: 30 minutes.
// With this optimization: 8 sec.
use std::io;
use std::path::Path;
use winapi_util::Handle;
fn handle_from_path_limited<P: AsRef<Path>>(path: P) -> io::Result<Handle> {
use std::fs::OpenOptions;
@@ -99,9 +102,9 @@ pub fn get_metadata(d: &DirEntry, _use_apparent_size: bool) -> Option<(u64, Opti
))
}
match d.metadata {
Some(Ok(ref md)) => {
use std::os::windows::fs::MetadataExt;
use std::os::windows::fs::MetadataExt;
match d.metadata() {
Ok(ref md) => {
const FILE_ATTRIBUTE_ARCHIVE: u32 = 0x20u32;
const FILE_ATTRIBUTE_READONLY: u32 = 0x1u32;
const FILE_ATTRIBUTE_HIDDEN: u32 = 0x2u32;
@@ -123,20 +126,3 @@ pub fn get_metadata(d: &DirEntry, _use_apparent_size: bool) -> Option<(u64, Opti
_ => get_metadata_expensive(&d),
}
}
#[cfg(target_family = "unix")]
pub fn get_filesystem<P: AsRef<Path>>(file_path: P) -> Result<u64, io::Error> {
use std::os::unix::fs::MetadataExt;
let metadata = fs::metadata(file_path)?;
Ok(metadata.dev())
}
#[cfg(target_family = "windows")]
pub fn get_filesystem<P: AsRef<Path>>(file_path: P) -> Result<u64, io::Error> {
use winapi_util::file::information;
use winapi_util::Handle;
let h = Handle::from_path_any(file_path)?;
let info = information(&h)?;
Ok(info.volume_serial_number())
}

View File

@@ -0,0 +1 @@
hi

View File

272
tests/test_exact_output.rs Normal file
View File

@@ -0,0 +1,272 @@
use assert_cmd::Command;
use std::str;
use std::sync::Once;
static INIT: Once = Once::new();
mod tests_symlinks;
/**
* This file contains tests that verify the exact output of the command.
* This output differs on Linux / Mac so the tests are harder to write and debug
* Windows is ignored here because the results vary by host making exact testing impractical
*
* Despite the above problems, these tests are good as they are the closest to 'the real thing'.
*/
// Warning: File sizes differ on both platform and on the format of the disk.
/// Copy to /tmp dir - we assume that the formatting of the /tmp partition
/// is consistent. If the tests fail your /tmp filesystem probably differs
fn copy_test_data(dir: &str) {
// First remove the existing directory - just incase it is there and has incorrect data
let last_slash = dir.rfind('/').unwrap();
let last_part_of_dir = dir.chars().skip(last_slash).collect::<String>();
match Command::new("rm")
.arg("-rf")
.arg("/tmp/".to_owned() + &*last_part_of_dir)
.ok()
{
Ok(_) => {}
Err(_) => {}
};
match Command::new("cp").arg("-r").arg(dir).arg("/tmp/").ok() {
Ok(_) => {}
Err(err) => {
eprintln!("Error copying directory {:?}", err);
}
};
}
pub fn initialize() {
INIT.call_once(|| {
copy_test_data("tests/test_dir");
copy_test_data("tests/test_dir2");
copy_test_data("tests/test_dir_unicode");
});
}
// "windows" result data can vary by host (size seems to be variable by one byte); fix code vs test and re-enable
#[cfg_attr(target_os = "windows", ignore)]
#[test]
pub fn test_main_basic() {
// -c is no color mode - This makes testing much simpler
initialize();
let mut cmd = Command::cargo_bin("dust").unwrap();
let assert = cmd.arg("-c").arg("/tmp/test_dir/").unwrap().stdout;
let output = str::from_utf8(&assert).unwrap();
assert!(output.contains(&main_output()));
}
#[cfg_attr(target_os = "windows", ignore)]
#[test]
pub fn test_main_multi_arg() {
initialize();
let mut cmd = Command::cargo_bin("dust").unwrap();
let assert = cmd
.arg("-c")
.arg("/tmp/test_dir/many/")
.arg("/tmp/test_dir")
.arg("/tmp/test_dir")
.unwrap()
.stdout;
let output = str::from_utf8(&assert).unwrap();
assert!(output.contains(&main_output()));
}
#[cfg(target_os = "macos")]
fn main_output() -> String {
r#"
0B ┌── a_file │░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░█ │ 0%
4.0K ├── hello_file│████████████████████████████████████████████████ │ 100%
4.0K ┌─┴ many │████████████████████████████████████████████████ │ 100%
4.0K ┌─┴ test_dir │████████████████████████████████████████████████ │ 100%
"#
.trim()
.to_string()
}
#[cfg(target_os = "linux")]
fn main_output() -> String {
r#"
0B ┌── a_file │ ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░█ │ 0%
4.0K ├── hello_file│ ░░░░░░░░░░░░░░░░█████████████████ │ 33%
8.0K ┌─┴ many │ █████████████████████████████████ │ 67%
12K ┌─┴ test_dir │████████████████████████████████████████████████ │ 100%
"#
.trim()
.to_string()
}
#[cfg(target_os = "windows")]
fn main_output() -> String {
"windows results vary by host".to_string()
}
#[cfg_attr(target_os = "windows", ignore)]
#[test]
pub fn test_main_long_paths() {
initialize();
let mut cmd = Command::cargo_bin("dust").unwrap();
let assert = cmd
.arg("-c")
.arg("-p")
.arg("/tmp/test_dir/")
.unwrap()
.stdout;
let output = str::from_utf8(&assert).unwrap();
println!("{:?}", output.trim());
println!("{:?}", main_output_long_paths().trim());
assert!(output.contains(&main_output_long_paths()));
}
#[cfg(target_os = "macos")]
fn main_output_long_paths() -> String {
r#"
0B ┌── /tmp/test_dir/many/a_file │░░░░░░░░░░░░░░░░░░░░░░░░░░░░█ │ 0%
4.0K ├── /tmp/test_dir/many/hello_file│█████████████████████████████ │ 100%
4.0K ┌─┴ /tmp/test_dir/many │█████████████████████████████ │ 100%
4.0K ┌─┴ /tmp/test_dir │█████████████████████████████ │ 100%
"#
.trim()
.to_string()
}
#[cfg(target_os = "linux")]
fn main_output_long_paths() -> String {
r#"
0B ┌── /tmp/test_dir/many/a_file │ ░░░░░░░░░░░░░░░░░░░█ │ 0%
4.0K ├── /tmp/test_dir/many/hello_file│ ░░░░░░░░░░██████████ │ 33%
8.0K ┌─┴ /tmp/test_dir/many │ ████████████████████ │ 67%
12K ┌─┴ /tmp/test_dir │█████████████████████████████ │ 100%
"#
.trim()
.to_string()
}
#[cfg(target_os = "windows")]
fn main_output_long_paths() -> String {
"windows results vary by host".to_string()
}
#[cfg_attr(target_os = "windows", ignore)]
#[test]
pub fn test_apparent_size() {
initialize();
let mut cmd = Command::cargo_bin("dust").unwrap();
let assert = cmd.arg("-c").arg("-s").arg("/tmp/test_dir").unwrap().stdout;
let output = str::from_utf8(&assert).unwrap();
assert!(output.contains(&output_apparent_size()));
}
#[cfg(target_os = "linux")]
fn output_apparent_size() -> String {
r#"
0B ┌── a_file │ ░░░░░░░░░░░░░░░░░░░░░░░░█ │ 0%
6B ├── hello_file│ ░░░░░░░░░░░░░░░░░░░░░░░░█ │ 0%
4.0K ┌─┴ many │ █████████████████████████ │ 50%
8.0K ┌─┴ test_dir │████████████████████████████████████████████████ │ 100%
"#
.trim()
.to_string()
}
#[cfg(target_os = "macos")]
fn output_apparent_size() -> String {
r#"
0B ┌── a_file │ ░░░░░░░░░░░░░░░░░░░░░░░░░░░█ │ 0%
6B ├── hello_file│ ░░░░░░░░░░░░░░░░░░░░░░░░░░██ │ 3%
134B ┌─┴ many │ ████████████████████████████ │ 58%
230B ┌─┴ test_dir │████████████████████████████████████████████████ │ 100%
"#
.trim()
.to_string()
}
#[cfg(target_os = "windows")]
fn output_apparent_size() -> String {
"windows results vary by host".to_string()
}
// Check against directories and files whos names are substrings of each other
#[cfg_attr(target_os = "windows", ignore)]
#[test]
pub fn test_substring_of_names_and_long_names() {
initialize();
let mut cmd = Command::cargo_bin("dust").unwrap();
let output = cmd.arg("-c").arg("/tmp/test_dir2").unwrap().stdout;
let output = str::from_utf8(&output).unwrap();
assert!(output.contains(&no_substring_of_names_output()));
}
#[cfg(target_os = "linux")]
fn no_substring_of_names_output() -> String {
"
0B ┌── long_dir_name_what_a_very_long_dir_name_what_happens_when_this_g..
4.0K ├── dir_name_clash
4.0K │ ┌── hello
8.0K ├─┴ dir_substring
4.0K │ ┌── hello
8.0K ├─┴ dir
24K ┌─┴ test_dir2
"
.trim()
.into()
}
#[cfg(target_os = "macos")]
fn no_substring_of_names_output() -> String {
"
0B ┌── long_dir_name_what_a_very_long_dir_name_what_happens_when_this_g..
4.0K │ ┌── hello
4.0K ├─┴ dir_substring
4.0K ├── dir_name_clash
4.0K │ ┌── hello
4.0K ├─┴ dir
12K ┌─┴ test_dir2
"
.trim()
.into()
}
#[cfg(target_os = "windows")]
fn no_substring_of_names_output() -> String {
"PRs".into()
}
#[cfg_attr(target_os = "windows", ignore)]
#[test]
pub fn test_unicode_directories() {
initialize();
let mut cmd = Command::cargo_bin("dust").unwrap();
let output = cmd.arg("-c").arg("/tmp/test_dir_unicode").unwrap().stdout;
let output = str::from_utf8(&output).unwrap();
assert!(output.contains(&unicode_dir()));
}
#[cfg(target_os = "linux")]
fn unicode_dir() -> String {
// The way unicode & asian characters are rendered on the terminal should make this line up
"
0B ┌── 👩.unicode │ █ │ 0%
0B ├── ラウトは難しいです!.japan│ █ │ 0%
4.0K ┌─┴ test_dir_unicode │██████████████████████████████████ │ 100%
"
.trim()
.into()
}
#[cfg(target_os = "macos")]
fn unicode_dir() -> String {
"
0B ┌── 👩.unicode │ █ │ 0%
0B ├── ラウトは難しいです!.japan│ █ │ 0%
0B ┌─┴ test_dir_unicode │ █ │ 0%
"
.trim()
.into()
}
#[cfg(target_os = "windows")]
fn unicode_dir() -> String {
"".into()
}

149
tests/test_flags.rs Normal file
View File

@@ -0,0 +1,149 @@
use assert_cmd::Command;
use std::str;
/**
* This file contains tests that test a substring of the output using '.contains'
*
* These tests should be the same cross platform
*/
// We can at least test the file names are there
#[test]
pub fn test_basic_output() {
let mut cmd = Command::cargo_bin("dust").unwrap();
let output = cmd.arg("tests/test_dir/").unwrap().stdout;
let output = str::from_utf8(&output).unwrap();
assert!(output.contains(" ┌─┴ "));
assert!(output.contains("test_dir "));
assert!(output.contains(" ┌─┴ "));
assert!(output.contains("many "));
assert!(output.contains(" ├── "));
assert!(output.contains("hello_file"));
assert!(output.contains(" ┌── "));
assert!(output.contains("a_file "));
}
#[test]
pub fn test_output_no_bars_means_no_excess_spaces() {
let mut cmd = Command::cargo_bin("dust").unwrap();
let output = cmd.arg("-b").arg("tests/test_dir/").unwrap().stdout;
let output = str::from_utf8(&output).unwrap();
// If bars are not being shown we don't need to pad the output with spaces
assert!(output.contains("many"));
assert!(!output.contains("many "));
}
#[test]
pub fn test_reverse_flag() {
let mut cmd = Command::cargo_bin("dust").unwrap();
let output = cmd
.arg("-c")
.arg("-r")
.arg("tests/test_dir/")
.unwrap()
.stdout;
let output = str::from_utf8(&output).unwrap();
assert!(output.contains(" └─┬ test_dir "));
assert!(output.contains(" └─┬ many "));
assert!(output.contains(" ├── hello_file"));
assert!(output.contains(" └── a_file "));
}
#[test]
pub fn test_d_flag_works() {
// We should see the top level directory but not the sub dirs / files:
let mut cmd = Command::cargo_bin("dust").unwrap();
let output = cmd
.arg("-d")
.arg("1")
.arg("-s")
.arg("tests/test_dir/")
.unwrap()
.stdout;
let output = str::from_utf8(&output).unwrap();
assert!(!output.contains("hello_file"));
}
#[test]
pub fn test_d_flag_works_and_still_recurses_down() {
// We had a bug where running with '-d 1' would stop at the first directory and the code
// would fail to recurse down
let mut cmd = Command::cargo_bin("dust").unwrap();
let output = cmd
.arg("-d")
.arg("1")
.arg("-f")
.arg("-c")
.arg("tests/test_dir2/")
.unwrap()
.stdout;
let output = str::from_utf8(&output).unwrap();
assert!(output.contains("7 ┌─┴ test_dir2"));
}
// Check against directories and files whos names are substrings of each other
#[test]
pub fn test_ignore_dir() {
let mut cmd = Command::cargo_bin("dust").unwrap();
let output = cmd
.arg("-c")
.arg("-X")
.arg("dir_substring")
.arg("tests/test_dir2")
.unwrap()
.stdout;
let output = str::from_utf8(&output).unwrap();
assert!(!output.contains("dir_substring"));
}
#[test]
pub fn test_with_bad_param() {
let mut cmd = Command::cargo_bin("dust").unwrap();
let stderr = cmd.arg("-").unwrap().stderr;
let stderr = str::from_utf8(&stderr).unwrap();
assert!(stderr.contains("Did not have permissions for all directories"));
}
#[test]
pub fn test_hidden_flag() {
// Check we can see the hidden file normally
let mut cmd = Command::cargo_bin("dust").unwrap();
let output = cmd
.arg("-c")
.arg("tests/test_dir_hidden_entries")
.unwrap()
.stdout;
let output = str::from_utf8(&output).unwrap();
assert!(output.contains(".hidden_file"));
assert!(output.contains("┌─┴ test_dir_hidden_entries"));
// Check that adding the '-h' flag causes us to not see hidden files
let mut cmd = Command::cargo_bin("dust").unwrap();
let output = cmd
.arg("-c")
.arg("-i")
.arg("tests/test_dir_hidden_entries")
.unwrap()
.stdout;
let output = str::from_utf8(&output).unwrap();
assert!(!output.contains(".hidden_file"));
assert!(output.contains("┌── test_dir_hidden_entries"));
}
#[test]
pub fn test_number_of_files() {
// Check we can see the hidden file normally
let mut cmd = Command::cargo_bin("dust").unwrap();
let output = cmd
.arg("-c")
.arg("-f")
.arg("tests/test_dir")
.unwrap()
.stdout;
let output = str::from_utf8(&output).unwrap();
assert!(output.contains("1 ┌── hello_file"));
assert!(output.contains("1 ├── a_file "));
assert!(output.contains("3 ┌─┴ many"));
assert!(output.contains("4 ┌─┴ test_dir"));
}

View File

@@ -1,267 +1 @@
mod tests_symlinks;
// File sizes differ on both platform and on the format of the disk.
// We can at least test the file names are there
#[test]
pub fn test_basic_output() {
assert_cli::Assert::main_binary()
.with_args(&["src/test_dir/"])
.stdout()
.contains(" ┌─┴ ")
.stdout()
.contains("test_dir ")
.stdout()
.contains(" ┌─┴ ")
.stdout()
.contains("many ")
.stdout()
.contains(" ├── ")
.stdout()
.contains("hello_file")
.stdout()
.contains(" ┌── ")
.stdout()
.contains("a_file ")
.unwrap();
}
// fix! [rivy; 2020-22-01] "windows" result data can vary by host (size seems to be variable by one byte); fix code vs test and re-enable
#[cfg_attr(target_os = "windows", ignore)]
#[test]
pub fn test_main_basic() {
// -c is no color mode - This makes testing much simpler
assert_cli::Assert::main_binary()
.with_args(&["-c", "src/test_dir"])
.stdout()
.is(main_output().as_str())
.unwrap();
}
// fix! [rivy; 2020-22-01] "windows" result data can vary by host (size seems to be variable by one byte); fix code vs test and re-enable
#[cfg_attr(target_os = "windows", ignore)]
#[test]
pub fn test_main_multi_arg() {
assert_cli::Assert::main_binary()
.with_args(&["-c", "src/test_dir/many/", "src/test_dir/", "src/test_dir"])
.stdout()
.is(main_output().as_str())
.unwrap();
}
#[cfg(target_os = "macos")]
fn main_output() -> String {
r#"
0B ┌── a_file │░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░█ │ 0%
4.0K ├── hello_file│██████████████████████████████████████████████ │ 100%
4.0K ┌─┴ many │██████████████████████████████████████████████ │ 100%
4.0K ┌─┴ test_dir │██████████████████████████████████████████████ │ 100%
"#
.to_string()
}
#[cfg(target_os = "linux")]
fn main_output() -> String {
r#"
0B ┌── a_file │ ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░█ │ 0%
4.0K ├── hello_file│ ░░░░░░░░░░░░░░░████████████████ │ 33%
8.0K ┌─┴ many │ ███████████████████████████████ │ 67%
12K ┌─┴ test_dir │██████████████████████████████████████████████ │ 100%
"#
.to_string()
}
#[cfg(target_os = "windows")]
fn main_output() -> String {
"PRs welcome".to_string()
}
// fix! [rivy; 2020-22-01] "windows" result data can vary by host (size seems to be variable by one byte); fix code vs test and re-enable
#[cfg_attr(target_os = "windows", ignore)]
#[test]
pub fn test_main_long_paths() {
assert_cli::Assert::main_binary()
.with_args(&["-c", "-p", "src/test_dir"])
.stdout()
.is(main_output_long_paths().as_str())
.unwrap();
}
#[cfg(target_os = "macos")]
fn main_output_long_paths() -> String {
r#"
0B ┌── src/test_dir/many/a_file │░░░░░░░░░░░░░░░░░░░░░░░░░░░█ │ 0%
4.0K ├── src/test_dir/many/hello_file│████████████████████████████ │ 100%
4.0K ┌─┴ src/test_dir/many │████████████████████████████ │ 100%
4.0K ┌─┴ src/test_dir │████████████████████████████ │ 100%
"#
.to_string()
}
#[cfg(target_os = "linux")]
fn main_output_long_paths() -> String {
r#"
0B ┌── src/test_dir/many/a_file │ ░░░░░░░░░░░░░░░░░░█ │ 0%
4.0K ├── src/test_dir/many/hello_file│ ░░░░░░░░░██████████ │ 33%
8.0K ┌─┴ src/test_dir/many │ ███████████████████ │ 67%
12K ┌─┴ src/test_dir │████████████████████████████ │ 100%
"#
.to_string()
}
#[cfg(target_os = "windows")]
fn main_output_long_paths() -> String {
"PRs welcome".to_string()
}
// fix! [rivy; 2020-22-01] "windows" result data can vary by host (size seems to be variable by one byte); fix code vs test and re-enable
#[cfg_attr(target_os = "windows", ignore)]
#[test]
pub fn test_apparent_size() {
assert_cli::Assert::main_binary()
.with_args(&["-c", "-s", "src/test_dir"])
.stdout()
.is(output_apparent_size().as_str())
.unwrap();
}
#[cfg(target_os = "linux")]
fn output_apparent_size() -> String {
r#"
0B ┌── a_file │ ░░░░░░░░░░░░░░░░░░░░░░░█ │ 0%
6B ├── hello_file│ ░░░░░░░░░░░░░░░░░░░░░░░█ │ 0%
4.0K ┌─┴ many │ ████████████████████████ │ 50%
8.0K ┌─┴ test_dir │██████████████████████████████████████████████ │ 100%
"#
.to_string()
}
#[cfg(target_os = "macos")]
fn output_apparent_size() -> String {
r#"
0B ┌── a_file │ ░░░░░░░░░░░░░░░░░░░░░░░░░░█ │ 0%
6B ├── hello_file│ ░░░░░░░░░░░░░░░░░░░░░░░░░██ │ 3%
134B ┌─┴ many │ ███████████████████████████ │ 58%
230B ┌─┴ test_dir │██████████████████████████████████████████████ │ 100%
"#
.to_string()
}
#[cfg(target_os = "windows")]
fn output_apparent_size() -> String {
"".to_string()
}
#[test]
pub fn test_reverse_flag() {
assert_cli::Assert::main_binary()
.with_args(&["-c", "-r", "src/test_dir/"])
.stdout()
.contains(" └─┬ test_dir ")
.stdout()
.contains(" └─┬ many ")
.stdout()
.contains(" ├── hello_file")
.stdout()
.contains(" └── a_file ")
.unwrap();
}
#[test]
pub fn test_d_flag_works() {
// We should see the top level directory but not the sub dirs / files:
assert_cli::Assert::main_binary()
.with_args(&["-d", "1", "-s", "src/test_dir"])
.stdout()
.doesnt_contain("hello_file")
.unwrap();
}
// Check against directories and files whos names are substrings of each other
// fix! [rivy; 2020-22-01] "windows" result data can vary by host (size seems to be variable by one byte); fix code vs test and re-enable
#[cfg_attr(target_os = "windows", ignore)]
#[test]
pub fn test_substring_of_names() {
assert_cli::Assert::main_binary()
.with_args(&["-c", "src/test_dir2"])
.stdout()
.is(no_substring_of_names_output().as_str())
.unwrap();
}
#[cfg(target_os = "linux")]
fn no_substring_of_names_output() -> String {
"
4.0K ┌── dir_name_clash│ ████████ │ 17%
4.0K │ ┌── hello │ ░░░░░░░████████ │ 17%
8.0K ├─┴ dir_substring │ ███████████████ │ 33%
4.0K │ ┌── hello │ ░░░░░░░████████ │ 17%
8.0K ├─┴ dir │ ███████████████ │ 33%
24K ┌─┴ test_dir2 │████████████████████████████████████████████ │ 100%
"
.into()
}
#[cfg(target_os = "macos")]
fn no_substring_of_names_output() -> String {
"
4.0K ┌── hello │ ███████████████ │ 33%
4.0K ┌─┴ dir_substring │ ███████████████ │ 33%
4.0K ├── dir_name_clash│ ███████████████ │ 33%
4.0K │ ┌── hello │ ███████████████ │ 33%
4.0K ├─┴ dir │ ███████████████ │ 33%
12K ┌─┴ test_dir2 │████████████████████████████████████████████ │ 100%
"
.into()
}
#[cfg(target_os = "windows")]
fn no_substring_of_names_output() -> String {
"PRs".into()
}
// fix! [rivy; 2020-22-01] "windows" result data can vary by host (size seems to be variable by one byte); fix code vs test and re-enable
#[cfg_attr(target_os = "windows", ignore)]
#[test]
pub fn test_unicode_directories() {
assert_cli::Assert::main_binary()
.with_args(&["-c", "src/test_dir3"])
.stdout()
.is(unicode_dir().as_str())
.unwrap();
}
#[cfg(target_os = "linux")]
fn unicode_dir() -> String {
// The way unicode & asian characters are rendered on the terminal should make this line up
"
0B ┌── 👩.unicode │ █ │ 0%
0B ├── ラウトは難しいです!.japan│ █ │ 0%
4.0K ┌─┴ test_dir3 │████████████████████████████████ │ 100%
"
.into()
}
#[cfg(target_os = "macos")]
fn unicode_dir() -> String {
"
0B ┌── 👩.unicode │ █ │ 0%
0B ├── ラウトは難しいです!.japan│ █ │ 0%
0B ┌─┴ test_dir3 │ █ │ 0%
"
.into()
}
#[cfg(target_os = "windows")]
fn unicode_dir() -> String {
"".into()
}
// Check against directories and files whos names are substrings of each other
#[test]
pub fn test_ignore_dir() {
assert_cli::Assert::main_binary()
.with_args(&["-c", "-X", "dir_substring", "src/test_dir2"])
.stdout()
.doesnt_contain("dir_substring")
.unwrap();
}

View File

@@ -1,14 +1,42 @@
use assert_cmd::Command;
use std::cmp::max;
use std::fs::File;
use std::io::Write;
use std::panic;
use std::path::PathBuf;
use std::process::Command;
use std::str;
use terminal_size::{terminal_size, Height, Width};
use unicode_width::UnicodeWidthStr;
use tempfile::Builder;
use tempfile::TempDir;
// File sizes differ on both platform and on the format of the disk.
// Windows: `ln` is not usually an available command; creation of symbolic links requires special enhanced permissions
fn get_width_of_terminal() -> u16 {
if let Some((Width(w), Height(_h))) = terminal_size() {
max(w, 80)
} else {
80
}
}
// Mac test runners create tmp files with very long names, hence it may be shortened in the output
fn get_file_name(name: String) -> String {
let terminal_plus_buffer = (get_width_of_terminal() - 14) as usize;
if UnicodeWidthStr::width(&*name) > terminal_plus_buffer {
let trimmed_name = name
.chars()
.take(terminal_plus_buffer - 2)
.collect::<String>();
trimmed_name + ".."
} else {
name
}
}
fn build_temp_file(dir: &TempDir) -> PathBuf {
let file_path = dir.path().join("notes.txt");
let mut file = File::create(&file_path).unwrap();
@@ -33,19 +61,18 @@ pub fn test_soft_sym_link() {
.output();
assert!(c.is_ok());
let c = format!(" ┌── {}", link_name_s);
let b = format!(" ├── {}", file_path_s);
let c = format!(" ┌── {}", get_file_name(link_name_s.into()));
let b = format!(" ├── {}", get_file_name(file_path_s.into()));
let a = format!("─┴ {}", dir_s);
assert_cli::Assert::main_binary()
.with_args(&["-p", "-c", &dir_s])
.stdout()
.contains(a.as_str())
.stdout()
.contains(b.as_str())
.stdout()
.contains(c.as_str())
.unwrap();
let mut cmd = Command::cargo_bin("dust").unwrap();
let output = cmd.arg("-p").arg("-c").arg(dir_s).unwrap().stdout;
let output = str::from_utf8(&output).unwrap();
assert!(output.contains(a.as_str()));
assert!(output.contains(b.as_str()));
assert!(output.contains(c.as_str()));
}
#[cfg_attr(target_os = "windows", ignore)]
@@ -64,30 +91,18 @@ pub fn test_hard_sym_link() {
.output();
assert!(c.is_ok());
let a = format!(" {}", dir_s);
let b = format!(" ┌── {}", link_name_s);
let b2 = format!(" ┌─{}", file_path_s);
let link_output = format!(" ┌─{}", get_file_name(link_name_s.into()));
let file_output = format!(" ┌── {}", get_file_name(file_path_s.into()));
let dirs_output = format!(" {}", dir_s);
let mut cmd = Command::cargo_bin("dust").unwrap();
let output = cmd.arg("-p").arg("-c").arg(dir_s).unwrap().stdout;
// Because this is a hard link the file and hard link look identical. Therefore
// we cannot guarantee which version will appear first.
let result = panic::catch_unwind(|| {
assert_cli::Assert::main_binary()
.with_args(&["-p", "-c", dir_s])
.stdout()
.contains(a.as_str())
.stdout()
.contains(b.as_str())
.unwrap();
});
if result.is_err() {
assert_cli::Assert::main_binary()
.with_args(&["-p", "-c", dir_s])
.stdout()
.contains(a.as_str())
.stdout()
.contains(b2.as_str())
.unwrap();
}
let output = str::from_utf8(&output).unwrap();
assert!(output.contains(dirs_output.as_str()));
assert!(output.contains(link_output.as_str()) || output.contains(file_output.as_str()));
}
#[cfg_attr(target_os = "windows", ignore)]
@@ -107,13 +122,12 @@ pub fn test_recursive_sym_link() {
assert!(c.is_ok());
let a = format!("─┬ {}", dir_s);
let b = format!(" └── {}", link_name_s);
let b = format!(" └── {}", get_file_name(link_name_s.into()));
assert_cli::Assert::main_binary()
.with_args(&["-c", "-r", "-p", dir_s])
.stdout()
.contains(a.as_str())
.stdout()
.contains(b.as_str())
.unwrap();
let mut cmd = Command::cargo_bin("dust").unwrap();
let output = cmd.arg("-p").arg("-c").arg("-r").arg(dir_s).unwrap().stdout;
let output = str::from_utf8(&output).unwrap();
assert!(output.contains(a.as_str()));
assert!(output.contains(b.as_str()));
}