Compare commits

..

39 Commits

Author SHA1 Message Date
andy.boot 6a86e8befd Increment version number 2018-05-09 11:46:42 +01:00
andy boot 3fb91a6c29 Merge pull request #19 from bootandy/rm_dup_input
code to remove duplicate arguments
2018-05-09 11:44:38 +01:00
andy.boot 51561994c5 Break up display_node function slightly
Also: run rustformat
2018-05-09 11:28:23 +01:00
andy.boot ce0e14bf00 Tweak output - the root node now has a: ─┬ 2018-05-09 10:58:32 +01:00
andy.boot dd75ec4aa7 wip: code to remove duplicate arguments
Also handle case where an argument is a substring of another argument
2018-05-09 10:35:30 +01:00
andy boot 65cd42736a Update README.md 2018-05-09 09:43:30 +01:00
andy boot 4792e97177 Merge pull request #18 from bootandy/fixes
Fixes
2018-05-02 09:51:21 +01:00
bootandy 3e9f09e339 Remove unnecessary path & pathbuf code 2018-05-02 00:42:03 +01:00
bootandy dba465a094 Tweak error message 2018-05-02 00:39:57 +01:00
bootandy 25d1ee7b43 Remove naked unwrap 2018-05-02 00:39:57 +01:00
bootandy 2556885622 Rust format 2018-05-02 00:39:57 +01:00
bootandy 8c088a7026 Fix: Passing a string into -n will no longer panic
fixes: #https://github.com/bootandy/dust/issues/16
2018-05-02 00:39:57 +01:00
bootandy 39db8b86fd Replace simple match with map_or 2018-05-01 14:38:34 +01:00
bootandy c5830c5d00 Stop using target_os just use target_family
target_family unix covers linux and mac and wraps up the call for inodes
in a common interface.
2018-05-01 13:59:48 +01:00
bootandy b68c450710 use eprintln! 2018-05-01 13:55:19 +01:00
andy.boot 6e2e5761d8 Increment version 2018-04-27 12:46:27 +01:00
andy boot 6842526d2c Merge pull request #15 from bootandy/depth2
Add support for -d depth flag
2018-04-27 12:28:15 +01:00
andy.boot 4ac85d7dc9 Simplify tests
dust sort is now more predictable as it orders first by size and second
by name.

Walkdir still gives different iteration orders on different systems so
all output is not entirely predictable
2018-04-27 11:35:47 +01:00
andy.boot 8170a07886 Increase default to 20 from 15
Clean up README a bit.
2018-04-27 10:23:20 +01:00
andy.boot 0f1f823736 Add long options to cmd line params 2018-04-27 10:15:30 +01:00
andy.boot e6c777fb8b Add support for -d depth flag
Following a user request the option '-d N' allows a user to output N
levels of sub directories.

Fixed bug: so that trailing slashes are now removed.
2018-04-27 10:01:41 +01:00
andy boot 0bded9698a Merge pull request #14 from bootandy/fix_mac
Fix tests on mac
2018-04-24 17:09:30 +01:00
bootandy c7f0ea59f0 Fix tests on mac
Macos does not appear to have a predictable iteration order of the files
2018-04-24 16:50:37 +01:00
andy boot 6d62cfb9ae Merge pull request #13 from bootandy/walkdir2
Rewrite to use walkdir instead of recursion
2018-04-24 15:41:51 +01:00
andy.boot 803934d84b By default only print last leaf of path
Fixes #9
https://github.com/bootandy/dust/issues/9

Instead of printing the all sub tree leaves only the last leaf is now
printed. See readme change for example

The flag '-p' was added to print the sub tree the old way
2018-04-24 14:54:11 +01:00
andy.boot 24c97ef92f Rewrite to use walkdir instead of recursion
Advised to use walkdir by burntsushi as using recursion on file systems
can blow the stack.

walkdir is slower but allows the code to be cleaner and more reliable

Also experimented with ignore but locking the hashmap resulted in
similar performance to walkdir but with much uglier code.
2018-04-24 14:53:47 +01:00
andy boot 270edf0a76 Update README.md 2018-04-18 13:49:05 +01:00
andy.boot 0c5b08e1d2 Change package name to du-dust
The binary is still called dust. Sadly dust was taken on crates.io.
This change should allow us to send this crate to crates.io
2018-04-18 13:40:52 +01:00
bootandy 35aef4c837 Add Cargo.lock 2018-04-16 23:09:04 +01:00
andy boot 95e7bb01eb Update README.md 2018-04-16 23:08:19 +01:00
andy boot 247e55788a Merge pull request #6 from Infinisil/unignore-lockfile
Don't ignore Cargo.lock
2018-04-16 22:54:07 +01:00
Silvan Mosberger bcab66ab8b Don't ignore Cargo.lock
See https://doc.rust-lang.org/cargo/faq.html#why-do-binaries-have-cargolock-in-version-control-but-not-libraries
2018-04-16 16:55:12 +02:00
andy boot 2c87a21da1 Merge pull request #5 from bootandy/refactor_orig
Refactor code
2018-04-07 17:17:54 +01:00
bootandy 74b9178017 Refactor code 2018-04-07 17:14:06 +01:00
andy boot 2eb1633b77 Update README.md 2018-04-07 11:53:55 +01:00
andy boot 3fd274ab36 Update README.md 2018-04-07 11:42:18 +01:00
andy boot f3ab902811 Update README.md 2018-04-07 11:38:21 +01:00
bootandy b3b1a867c4 Fix tests on CI
Files do not appear in predictable order - this order differs on
different versions of linux :-(.
2018-04-07 11:03:31 +01:00
bootandy 32561ecb18 Fix tests for linux 2018-04-07 10:23:47 +01:00
10 changed files with 1100 additions and 348 deletions
+1 -4
View File
@@ -2,10 +2,7 @@
# will have compiled files and executables # will have compiled files and executables
/target/ /target/
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock
Cargo.lock
# These are backup files generated by rustfmt # These are backup files generated by rustfmt
**/*.rs.bk **/*.rs.bk
*.swp *.swp
.vscode/*
Generated
+511
View File
@@ -0,0 +1,511 @@
[[package]]
name = "ansi_term"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "assert_cli"
version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"colored 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"difference 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"environment 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)",
"skeptic 0.13.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "atty"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
"termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "backtrace"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"backtrace-sys 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-demangle 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "backtrace-sys"
version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cc 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "bitflags"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "bitflags"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "bytecount"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "cargo_metadata"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"semver 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "cc"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "cfg-if"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "clap"
version = "2.31.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"atty 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
"bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"textwrap 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
"vec_map 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "colored"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "difference"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "dtoa"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "du-dust"
version = "0.3.1"
dependencies = [
"ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"assert_cli 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)",
"clap 2.31.1 (registry+https://github.com/rust-lang/crates.io-index)",
"tempfile 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"walkdir 2.1.4 (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 = "error-chain"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"backtrace 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "fuchsia-zircon"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "fuchsia-zircon-sys"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "glob"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "itoa"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "kernel32-sys"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "lazy_static"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "libc"
version = "0.2.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "num-traits"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "proc-macro2"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "pulldown-cmark"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "quote"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "redox_syscall"
version = "0.1.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "redox_termios"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "remove_dir_all"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rustc-demangle"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "same-file"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "same-file"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "semver"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.37 (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.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "serde_derive"
version = "1.0.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive_internals 0.23.0 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "serde_derive_internals"
version = "0.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "serde_json"
version = "1.0.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"itoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "skeptic"
version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bytecount 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cargo_metadata 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
"pulldown-cmark 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)",
"tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
"walkdir 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "strsim"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "syn"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "tempdir"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"remove_dir_all 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "tempfile"
version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)",
"remove_dir_all 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "termion"
version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
"redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)",
"redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "textwrap"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "unicode-width"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "unicode-xid"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "vec_map"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "walkdir"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"same-file 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "walkdir"
version = "2.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"same-file 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "winapi"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "winapi"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
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)",
]
[[package]]
name = "winapi-build"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[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 assert_cli 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "72342c21057a3cb5f7c2d849bf7999a83795434dd36d74fa8c24680581bd1930"
"checksum atty 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "af80143d6f7608d746df1520709e5d141c96f240b0e62b0aa41bdfb53374d9d4"
"checksum backtrace 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ebbbf59b1c43eefa8c3ede390fcc36820b4999f7914104015be25025e0d62af2"
"checksum backtrace-sys 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "44585761d6161b0f57afc49482ab6bd067e4edef48c12a152c237eb0203f7661"
"checksum bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5"
"checksum bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b3c30d3802dfb7281680d6285f2ccdaa8c2d8fee41f93805dba5c4cf50dc23cf"
"checksum bytecount 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "af27422163679dea46a1a7239dffff64d3dcdc3ba5fe9c49c789fbfe0eb949de"
"checksum cargo_metadata 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1f56ec3e469bca7c276f2eea015aa05c5e381356febdbb0683c2580189604537"
"checksum cc 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)" = "2b4911e4bdcb4100c7680e7e854ff38e23f1b34d4d9e079efae3da2801341ffc"
"checksum cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c819a1287eb618df47cc647173c5c4c66ba19d888a6e50d605672aed3140de"
"checksum clap 2.31.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5dc18f6f4005132120d9711636b32c46a233fad94df6217fa1d81c5e97a9f200"
"checksum colored 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b0aa3473e85a3161b59845d6096b289bb577874cafeaf75ea1b1beaa6572c7fc"
"checksum difference 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b3304d19798a8e067e48d8e69b2c37f0b5e9b4e462504ad9e27e9f3fce02bba8"
"checksum dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "09c3753c3db574d215cba4ea76018483895d7bff25a31b49ba45db21c48e50ab"
"checksum environment 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1f4b14e20978669064c33b4c1e0fb4083412e40fe56cbea2eae80fd7591503ee"
"checksum error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff511d5dc435d703f4971bc399647c9bc38e20cb41452e3b9feb4765419ed3f3"
"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
"checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb"
"checksum itoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c069bbec61e1ca5a596166e55dfe4773ff745c3d16b700013bcaff9a6df2c682"
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
"checksum lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "76f033c7ad61445c5b347c7382dd1237847eb1bce590fe50365dcb33d546be73"
"checksum libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)" = "f54263ad99207254cf58b5f701ecb432c717445ea2ee8af387334bdd1a03fdff"
"checksum num-traits 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dee092fcdf725aee04dd7da1d21debff559237d49ef1cb3e69bcb8ece44c7364"
"checksum proc-macro2 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "681c2c8e039ff358cb926dbc5151d561cbd0249089986ace39dfe8e405bb7511"
"checksum pulldown-cmark 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d6fdf85cda6cadfae5428a54661d431330b312bc767ddbc57adbedc24da66e32"
"checksum quote 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7b0ff51282f28dc1b53fd154298feaa2e77c5ea0dba68e1fd8b03b72fbe13d2a"
"checksum rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "eba5f8cb59cc50ed56be8880a5c7b496bfd9bd26394e176bc67884094145c2c5"
"checksum redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "0d92eecebad22b767915e4d529f89f28ee96dbbf5a4810d2b844373f136417fd"
"checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76"
"checksum remove_dir_all 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "dfc5b3ce5d5ea144bb04ebd093a9e14e9765bcfec866aecda9b6dec43b3d1e24"
"checksum rustc-demangle 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "11fb43a206a04116ffd7cfcf9bcb941f8eb6cc7ff667272246b0a1c74259a3cb"
"checksum same-file 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d931a44fdaa43b8637009e7632a02adc4f2b2e0733c08caa4cf00e8da4a117a7"
"checksum same-file 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "cfb6eded0b06a0b512c8ddbcf04089138c9b4362c2f696f3c3d76039d68f3637"
"checksum semver 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bee2bc909ab2d8d60dab26e8cad85b25d795b14603a0dcb627b78b9d30b6454b"
"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
"checksum serde 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)" = "d3bcee660dcde8f52c3765dd9ca5ee36b4bf35470a738eb0bd5a8752b0389645"
"checksum serde_derive 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)" = "f1711ab8b208541fa8de00425f6a577d90f27bb60724d2bb5fd911314af9668f"
"checksum serde_derive_internals 0.23.0 (registry+https://github.com/rust-lang/crates.io-index)" = "89b340a48245bc03ddba31d0ff1709c118df90edc6adabaca4aac77aea181cce"
"checksum serde_json 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)" = "5c508584d9913df116b91505eec55610a2f5b16e9ed793c46e4d0152872b3e74"
"checksum skeptic 0.13.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c8431f8fca168e2db4be547bd8329eac70d095dff1444fee4b0fa0fabc7df75a"
"checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550"
"checksum syn 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)" = "91b52877572087400e83d24b9178488541e3d535259e04ff17a63df1e5ceff59"
"checksum tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8"
"checksum tempfile 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "439d9a7c00f98b1b5ee730039bf5b1f9203d508690e3c76b509e7ad59f8f7c99"
"checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096"
"checksum textwrap 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c0b59b6b4b44d867f1370ef1bd91bfb262bf07bf0ae65c202ea2fbc16153b693"
"checksum unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "bf3a113775714a22dcb774d8ea3655c53a32debae63a063acc00a91cc586245f"
"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
"checksum vec_map 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "887b5b631c2ad01628bbbaa7dd4c869f80d3186688f8d0b6f58774fbe324988c"
"checksum walkdir 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "bb08f9e670fab86099470b97cd2b252d6527f0b3cc1401acdb595ffc9dd288ff"
"checksum walkdir 2.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "63636bd0eb3d00ccb8b9036381b526efac53caf112b7783b730ab3f8e44da369"
"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
"checksum winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "04e3bd221fcbe8a271359c04f21a76db7d0c6028862d1bb5512d85e1e2eb5bb3"
"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+19 -2
View File
@@ -1,11 +1,28 @@
[package] [package]
name = "dust" name = "du-dust"
version = "0.2.3" description = "A more intuitive version of du"
version = "0.3.1"
authors = ["bootandy <bootandy@gmail.com>", "nebkor <code@ardent.nebcorp.com>"] authors = ["bootandy <bootandy@gmail.com>", "nebkor <code@ardent.nebcorp.com>"]
documentation = "https://github.com/bootandy/dust"
homepage = "https://github.com/bootandy/dust"
repository = "https://github.com/bootandy/dust"
keywords = ["du", "command-line", "disk", "disk-usage"]
categories = ["command-line-utilities"]
license = "Apache-2.0"
[badges]
travis-ci = {repository = "https://travis-ci.org/bootandy/dust"}
[[bin]]
name = "dust"
path = "src/main.rs"
[dependencies] [dependencies]
ansi_term = "0.11" ansi_term = "0.11"
clap = "2.31" clap = "2.31"
assert_cli = "0.5" assert_cli = "0.5"
tempfile = "3" tempfile = "3"
walkdir = "2"
+54 -33
View File
@@ -1,51 +1,72 @@
[![Build Status](https://travis-ci.org/bootandy/dust.svg?branch=master)](https://travis-ci.org/bootandy/dust) [![Build Status](https://travis-ci.org/bootandy/dust.svg?branch=master)](https://travis-ci.org/bootandy/dust)
# Dust # Dust
du + rust = dust. A rust alternative to du
[Releases](https://github.com/bootandy/dust/releases) du + rust = dust. Like du but more intuitive
To install:
* Download linux / mac binary from [Releases](https://github.com/bootandy/dust/releases)
* unzip file: tar -xvf <file>
* copy file to search path: sudo mv dust /usr/local/bin/
Unlike du, dust is meant to give you an instant overview of which directories are using disk space without requiring sort or head. Dust does not count file system blocks; it uses file sizes instead. Dust will print a maximum of 1 'Did not have permissions message'. ## Install
#### Cargo Install
Dust will list the 15 biggest sub directories and will smartly recurse down the tree to find the larger ones. There is no need for a '-d' flag or a '-h' flag. The largest sub directory will have its size shown in red * cargo install du-dust
#### Download Install
* Download linux / mac binary from [Releases](https://github.com/bootandy/dust/releases)
* unzip file: tar -xvf _downloaded_file.tar.gz_
* move file to executable path: sudo mv dust /usr/local/bin/
## Overview
Dust is meant to give you an instant overview of which directories are using disk space without requiring sort or head. Dust will print a maximum of 1 'Did not have permissions message'.
Dust will list the 20 biggest sub directories or files and will smartly recurse down the tree to find the larger ones. There is no need for a '-d' flag or a '-h' flag. The largest sub directory will have its size shown in *red*
## Why?
du has a number of ways of showing you what it finds, in terms of disk consumption, but really, there are only one or two ways you invoke it: with -h for “human readable” units, like 100G or 89k, or with -b for “bytes”. The former is generally used for a quick survey of a directory with a small number of things in it, and the latter for when you have a bunch and need to sort the output numerically, and youre obligated to either further pass it into something like awk to turn bytes into the appropriate human-friendly unit like mega or gigabytes, or pipe thru sort and head while remembering the '-h' flag. Then once you have the top offenders, you recurse down into the largest one and repeat the process until youve found your cruft or gems and can move on.
Dust assumes thats what you wanted to do in the first place, and takes care of tracking the largest offenders in terms of actual size, and showing them to you with human-friendly units and in-context within the filetree.
## Usage
``` ```
Usage: dust <dir> Usage: dust <dir>
Usage: dust <dir> <another_dir> <and_more> Usage: dust <dir> <another_dir> <and_more>
Usage: dust -s <dir> (apparent-size - shows the length of the file as opposed to the amount of disk space it uses) Usage: dust -p <dir> (full-path - does not shorten the path of the subdirectories)
Usage: dust -n 30 <dir> (Shows 30 directories not 15) Usage: dust -s <dir> (apparent-size - shows the length of the file as opposed to the amount of disk space it uses)
Usage: dust -n 30 <dir> (Shows 30 directories not 20)
Usage: dust -d 3 <dir> (Shows 3 levels of subdirectories)
``` ```
``` ```
djin:git/dust> dust djin:git/dust> dust
65M . 1.2G target
65M ─┬ ./target 622M ─┬ debug
49M ├─┬ ./target/debug 445M ├── deps
26M │ ├─┬ ./target/debug/deps 70M │ ├── incremental
21M │ │ └── ./target/debug/deps/libclap-9e6625ac8ff074ad.rlib 56M │ └── build
13M │ ├── ./target/debug/dust 262M ├─┬ rls
8.9M │ └─┬ ./target/debug/incremental 262M │ └─┬ debug
6.7M │ ├─┬ ./target/debug/incremental/dust-2748eiei2tcnp 203M │ ├── deps
6.7M │ │ └─┬ ./target/debug/incremental/dust-2748eiei2tcnp/s-ezd6jnik5u-163pyem-1aab9ncf5glum 56M │ └── build
3.0M │ │ └── ./target/debug/incremental/dust-2748eiei2tcnp/s-ezd6jnik5u-163pyem-1aab9ncf5glum/dep-graph.bin 165M ├─┬ package
2.2M │ └─┬ ./target/debug/incremental/dust-1dlon65p8m3vl 165M │ └─┬ du-dust-0.2.4
2.2M │ └── ./target/debug/incremental/dust-1dlon65p8m3vl/s-ezd6jncecv-1xsnfd0-4dw9l1r2th2t 165M │ └─target
15M └─┬ ./target/release 165M └─┬ debug
9.2M ├─┬ ./target/release/deps 131M └── deps
6.7M │ └── ./target/release/deps/libclap-87bc2534ea57f044.rlib 165M └─┬ release
5.9M └── ./target/release/dust 124M └── deps
``` ```
Performance: dust is currently about 4 times slower than du.
Alternatives: ## Performance
* [NCDU](https://dev.yorhel.nl/ncdu)
* du -d 1 -h | sort -h Dust is currently about 4 times slower than du.
## Alternatives
* [NCDU](https://dev.yorhel.nl/ncdu)
* du -d 1 -h | sort -h
Note: Apparent-size is calculated slightly differently in dust to gdu. In dust each hard link is counted as using file_length space. In gdu only the first entry is counted. Note: Apparent-size is calculated slightly differently in dust to gdu. In dust each hard link is counted as using file_length space. In gdu only the first entry is counted.
+134 -54
View File
@@ -1,90 +1,170 @@
extern crate ansi_term; extern crate ansi_term;
use self::ansi_term::Colour::Fixed; use self::ansi_term::Colour::Fixed;
use dust::Node; use std::collections::HashSet;
static UNITS: [char; 4] = ['T', 'G', 'M', 'K']; static UNITS: [char; 4] = ['T', 'G', 'M', 'K'];
pub fn draw_it(permissions: bool, heads: &Vec<Node>, to_display: &Vec<&Node>) -> () { pub fn draw_it(
permissions: bool,
short_paths: bool,
depth: Option<u64>,
base_dirs: HashSet<String>,
to_display: Vec<(String, u64)>,
) -> () {
if !permissions { if !permissions {
eprintln!("Did not have permissions for all directories"); eprintln!("Did not have permissions for all directories");
} }
let mut found = HashSet::new();
for d in to_display { for &(ref k, _) in to_display.iter() {
if heads.contains(d) { if base_dirs.contains(k) {
display_node(d, &to_display, true, "") display_node(&k, &mut found, &to_display, true, short_paths, depth, "─┬")
} }
} }
} }
fn get_size(nodes: &Vec<(String, u64)>, node_to_print: &String) -> Option<u64> {
for &(ref k, ref v) in nodes.iter() {
if *k == *node_to_print {
return Some(*v);
}
}
None
}
fn display_node<S: Into<String>>( fn display_node<S: Into<String>>(
node_to_print: &Node, node_to_print: &String,
to_display: &Vec<&Node>, found: &mut HashSet<String>,
is_first: bool, to_display: &Vec<(String, u64)>,
is_biggest: bool,
short_paths: bool,
depth: Option<u64>,
indentation_str: S, indentation_str: S,
) { ) {
let mut is = indentation_str.into(); if found.contains(node_to_print) {
print_this_node(node_to_print, is_first, is.as_ref()); return;
}
found.insert(node_to_print.to_string());
is = is.replace("└─┬", " "); let new_depth = match depth {
is = is.replace("└──", " "); None => None,
is = is.replace("├──", ""); Some(0) => return,
is = is.replace("├─┬", ""); Some(d) => Some(d - 1),
};
match get_size(to_display, node_to_print) {
None => println!("Can not find path: {}", node_to_print),
Some(size) => {
let is = indentation_str.into();
let ntp: &str = node_to_print.as_ref();
let printable_node_slashes = node_to_print.name().matches('/').count(); print_this_node(ntp, size, is_biggest, short_paths, is.as_ref());
let new_indent_str = clean_indentation_string(is);
let mut num_siblings = to_display.iter().fold(0, |a, b| { let num_slashes = node_to_print.matches('/').count();
if node_to_print.children().contains(b) let mut num_siblings = count_siblings(to_display, num_slashes, ntp);
&& b.name().matches('/').count() == printable_node_slashes + 1
{
a + 1
} else {
a
}
});
let mut is_biggest = true; let mut is_biggest = true;
for node in to_display { for &(ref k, _) in to_display.iter() {
if node_to_print.children().contains(node) { if k.starts_with(ntp) && k.matches('/').count() == num_slashes + 1 {
let has_display_children = node.children() num_siblings -= 1;
.iter()
.fold(false, |has_kids, n| has_kids || to_display.contains(&n));
let has_children = node.children().len() > 0 && has_display_children; let mut has_children = false;
if node.name().matches('/').count() == printable_node_slashes + 1 { if new_depth.is_none() || new_depth.unwrap() != 1 {
num_siblings -= 1; for &(ref k2, _) in to_display.iter() {
let kk: &str = k.as_ref();
let tree_chars = { if k2.starts_with(kk) && k2.matches('/').count() == num_slashes + 2 {
if num_siblings == 0 { has_children = true;
if has_children { }
"└─┬"
} else {
"└──"
}
} else {
if has_children {
"├─┬"
} else {
"├──"
} }
} }
};
display_node(&node, to_display, is_biggest, is.to_string() + tree_chars); display_node(
is_biggest = false; k,
found,
to_display,
is_biggest,
short_paths,
new_depth,
new_indent_str.to_string() + get_tree_chars(num_siblings, has_children),
);
is_biggest = false;
}
} }
} }
} }
} }
fn print_this_node(node: &Node, is_biggest: bool, indentation: &str) { fn clean_indentation_string<S: Into<String>>(s: S) -> String {
let pretty_size = format!("{:>5}", human_readable_number(node.size()),); let mut is = s.into();
is = is.replace("└─┬", " ");
is = is.replace("└──", " ");
is = is.replace("├──", "");
is = is.replace("├─┬", "");
is = is.replace("─┬", " ");
is
}
fn count_siblings(to_display: &Vec<(String, u64)>, num_slashes: usize, ntp: &str) -> u64 {
to_display.iter().fold(0, |a, b| {
if b.0.starts_with(ntp) && b.0.matches('/').count() == num_slashes + 1 {
a + 1
} else {
a
}
})
}
fn get_tree_chars(num_siblings: u64, has_children: bool) -> &'static str {
if num_siblings == 0 {
if has_children {
"└─┬"
} else {
"└──"
}
} else {
if has_children {
"├─┬"
} else {
"├──"
}
}
}
fn print_this_node(
node_name: &str,
size: u64,
is_biggest: bool,
short_paths: bool,
indentation: &str,
) {
let pretty_size = format!("{:>5}", human_readable_number(size),);
println!( println!(
"{}", "{}",
format_string(node.name(), is_biggest, pretty_size.as_ref(), indentation) format_string(
node_name,
is_biggest,
short_paths,
pretty_size.as_ref(),
indentation
)
) )
} }
pub fn format_string(dir_name: &str, is_biggest: bool, size: &str, indentation: &str) -> String { pub fn format_string(
dir_name: &str,
is_biggest: bool,
short_paths: bool,
size: &str,
indentation: &str,
) -> String {
let printable_name = {
if short_paths {
dir_name.split('/').last().unwrap_or(dir_name)
} else {
dir_name
}
};
format!( format!(
"{} {} {}", "{} {} {}",
if is_biggest { if is_biggest {
@@ -93,7 +173,7 @@ pub fn format_string(dir_name: &str, is_biggest: bool, size: &str, indentation:
Fixed(7).paint(size) Fixed(7).paint(size)
}, },
indentation, indentation,
dir_name, printable_name,
) )
} }
-68
View File
@@ -1,68 +0,0 @@
use std::cmp::{Eq, Ord, Ordering, PartialEq, PartialOrd};
#[derive(Clone, Debug)]
pub struct Node {
name: String,
size: u64,
children: Vec<Node>,
}
impl Node {
pub fn new<S: Into<String>>(name: S, size: u64, children: Vec<Node>) -> Self {
Node {
children: children,
name: name.into(),
size: size,
}
}
pub fn children(&self) -> &Vec<Node> {
&self.children
}
pub fn name(&self) -> &String {
&self.name
}
pub fn size(&self) -> u64 {
self.size
}
}
impl Ord for Node {
fn cmp(&self, other: &Self) -> Ordering {
if self.size > other.size {
Ordering::Less
} else if self.size < other.size {
Ordering::Greater
} else {
let my_slashes = self.name.matches('/').count();
let other_slashes = other.name.matches('/').count();
if my_slashes > other_slashes {
Ordering::Greater
} else if my_slashes < other_slashes {
Ordering::Less
} else {
if self.name < other.name {
Ordering::Less
} else if self.name > other.name {
Ordering::Greater
} else {
Ordering::Equal
}
}
}
}
}
impl PartialOrd for Node {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl PartialEq for Node {
fn eq(&self, other: &Self) -> bool {
(&self.name, self.size) == (&other.name, other.size)
}
}
impl Eq for Node {}
+72 -15
View File
@@ -1,47 +1,104 @@
#[macro_use] #[macro_use]
extern crate clap; extern crate clap;
extern crate assert_cli; extern crate assert_cli;
extern crate dust; extern crate walkdir;
use self::display::draw_it; use self::display::draw_it;
use clap::{App, AppSettings, Arg}; use clap::{App, AppSettings, Arg};
use utils::{find_big_ones, get_dir_tree}; use utils::{find_big_ones, get_dir_tree, simplify_dir_names, sort};
mod display; mod display;
mod utils; mod utils;
static DEFAULT_NUMBER_OF_LINES: &'static str = "15"; static DEFAULT_NUMBER_OF_LINES: usize = 20;
fn main() { fn main() {
let def_num_str = DEFAULT_NUMBER_OF_LINES.to_string();
let options = App::new("Dust") let options = App::new("Dust")
.setting(AppSettings::TrailingVarArg) .setting(AppSettings::TrailingVarArg)
.arg( .arg(
Arg::with_name("number_of_lines") Arg::with_name("depth")
.short("n") .short("d")
.help("Number of lines of output to show") .long("depth")
.takes_value(true) .help("Depth to show")
.default_value(DEFAULT_NUMBER_OF_LINES), .takes_value(true),
) )
.arg( .arg(
Arg::with_name("use_apparent_size") Arg::with_name("number_of_lines")
.short("n")
.long("number-of-lines")
.help("Number of lines of output to show")
.takes_value(true)
.default_value(def_num_str.as_ref()),
)
.arg(
Arg::with_name("display_full_paths")
.short("p")
.long("full-paths")
.help("If set sub directories will not have their path shortened"),
)
.arg(
Arg::with_name("display_apparent_size")
.short("s") .short("s")
.long("apparent-size")
.help("If set will use file length. Otherwise we use blocks"), .help("If set will use file length. Otherwise we use blocks"),
) )
.arg(Arg::with_name("inputs").multiple(true)) .arg(Arg::with_name("inputs").multiple(true))
.get_matches(); .get_matches();
let filenames = { let target_dirs = {
match options.values_of("inputs") { match options.values_of("inputs") {
None => vec!["."], None => vec!["."],
Some(r) => r.collect(), Some(r) => r.collect(),
} }
}; };
let number_of_lines = value_t!(options.value_of("number_of_lines"), usize).unwrap();
let use_apparent_size = options.is_present("use_apparent_size");
let (permissions, node_per_top_level_dir) = get_dir_tree(&filenames, use_apparent_size); let number_of_lines = match value_t!(options.value_of("number_of_lines"), usize) {
let slice_it = find_big_ones(&node_per_top_level_dir, number_of_lines); Ok(v) => v,
draw_it(permissions, &node_per_top_level_dir, &slice_it); Err(_) => {
eprintln!("Ignoring bad value for number_of_lines");
DEFAULT_NUMBER_OF_LINES
}
};
let depth = {
if options.is_present("depth") {
match value_t!(options.value_of("depth"), u64) {
Ok(v) => Some(v + 1),
Err(_) => {
eprintln!("Ignoring bad value for depth");
None
}
}
} else {
None
}
};
if options.is_present("depth") && number_of_lines != DEFAULT_NUMBER_OF_LINES {
eprintln!("Use either -n or -d. Not both");
return;
}
let use_apparent_size = options.is_present("display_apparent_size");
let use_full_path = options.is_present("display_full_paths");
let simplified_dirs = simplify_dir_names(target_dirs);
let (permissions, nodes, top_level_names) = get_dir_tree(simplified_dirs, use_apparent_size);
let sorted_data = sort(nodes);
let biggest_ones = {
if depth.is_none() {
find_big_ones(sorted_data, number_of_lines)
} else {
sorted_data
}
};
draw_it(
permissions,
!use_full_path,
depth,
top_level_names,
biggest_ones,
);
} }
#[cfg(test)] #[cfg(test)]
+175 -33
View File
@@ -6,34 +6,100 @@ use super::*;
use display::format_string; use display::format_string;
use std::fs::File; use std::fs::File;
use std::io::Write; use std::io::Write;
use std::panic;
use std::path::PathBuf; use std::path::PathBuf;
use std::process::Command; use std::process::Command;
#[test] #[test]
pub fn test_main() { pub fn test_main() {
let r = format!( assert_cli::Assert::main_binary()
.with_args(&["src/test_dir"])
.stdout()
.is(main_output(true))
.unwrap();
}
#[test]
pub fn test_main_long_paths() {
assert_cli::Assert::main_binary()
.with_args(&["-p", "src/test_dir"])
.stdout()
.is(main_output(false))
.unwrap();
}
#[test]
pub fn test_main_multi_arg() {
assert_cli::Assert::main_binary()
.with_args(&["src/test_dir/many/", "src/test_dir/", "src/test_dir"])
.stdout()
.is(main_output(true))
.unwrap();
}
#[cfg(target_os = "macos")]
fn main_output(short_paths: bool) -> String {
format!(
"{} "{}
{} {}
{} {}
{}", {}",
format_string("src/test_dir", true, " 4.0K", ""), format_string("src/test_dir", true, short_paths, " 4.0K", "─┬"),
format_string("src/test_dir/many", true, " 4.0K", "└─┬",), format_string("src/test_dir/many", true, short_paths, " 4.0K", " └─┬",),
format_string("src/test_dir/many/hello_file", true, " 4.0K", " ├──",), format_string(
format_string("src/test_dir/many/a_file", false, " 0B", " └──",), "src/test_dir/many/hello_file",
); true,
short_paths,
" 4.0K",
" ├──",
),
format_string(
"src/test_dir/many/a_file",
false,
short_paths,
" 0B",
" └──",
),
)
}
assert_cli::Assert::main_binary() #[cfg(target_os = "linux")]
.with_args(&["src/test_dir"]) fn main_output(short_paths: bool) -> String {
.stdout() format!(
.is(r) "{}
.unwrap(); {}
{}
{}",
format_string("src/test_dir", true, short_paths, " 12K", "─┬"),
format_string("src/test_dir/many", true, short_paths, " 8.0K", " └─┬",),
format_string(
"src/test_dir/many/hello_file",
true,
short_paths,
" 4.0K",
" ├──",
),
format_string(
"src/test_dir/many/a_file",
false,
short_paths,
" 0B",
" └──",
),
)
} }
#[test] #[test]
pub fn test_apparent_size() { pub fn test_apparent_size() {
let r = format!( let r = format!(
"{}", "{}",
format_string("src/test_dir/many/hello_file", true, " 6B", " ├──",), format_string(
"src/test_dir/many/hello_file",
true,
true,
" 6B",
" ├──",
),
); );
assert_cli::Assert::main_binary() assert_cli::Assert::main_binary()
@@ -66,15 +132,10 @@ pub fn test_soft_sym_link() {
.output(); .output();
assert!(c.is_ok()); assert!(c.is_ok());
let r = format!( let r = soft_sym_link_output(dir_s, file_path_s, link_name_s);
"{}
{}
{}",
format_string(dir_s, true, " 8.0K", ""),
format_string(file_path_s, true, " 4.0K", "├──",),
format_string(link_name_s, false, " 4.0K", "└──",),
);
// We cannot guarantee which version will appear first.
// TODO: Consider adding predictable itteration order (sort file entries by name?)
assert_cli::Assert::main_binary() assert_cli::Assert::main_binary()
.with_args(&[dir_s]) .with_args(&[dir_s])
.stdout() .stdout()
@@ -82,6 +143,30 @@ pub fn test_soft_sym_link() {
.unwrap(); .unwrap();
} }
#[cfg(target_os = "macos")]
fn soft_sym_link_output(dir: &str, file_path: &str, link_name: &str) -> String {
format!(
"{}
{}
{}",
format_string(dir, true, true, " 8.0K", "─┬"),
format_string(file_path, true, true, " 4.0K", " ├──",),
format_string(link_name, false, true, " 4.0K", " └──",),
)
}
#[cfg(target_os = "linux")]
fn soft_sym_link_output(dir: &str, file_path: &str, link_name: &str) -> String {
format!(
"{}
{}
{}",
format_string(dir, true, true, " 8.0K", "─┬"),
format_string(file_path, true, true, " 4.0K", " ├──",),
format_string(link_name, false, true, " 0B", " └──",),
)
}
// Hard links are ignored as the inode is the same as the file // Hard links are ignored as the inode is the same as the file
#[test] #[test]
pub fn test_hard_sym_link() { pub fn test_hard_sym_link() {
@@ -98,18 +183,59 @@ pub fn test_hard_sym_link() {
.output(); .output();
assert!(c.is_ok()); assert!(c.is_ok());
let (r, r2) = hard_link_output(dir_s, file_path_s, link_name_s);
// Because this is a hard link the file and hard link look identicle. Therefore
// we cannot guarantee which version will appear first.
// TODO: Consider adding predictable itteration order (sort file entries by name?)
let result = panic::catch_unwind(|| {
assert_cli::Assert::main_binary()
.with_args(&[dir_s])
.stdout()
.contains(r)
.unwrap();
});
if result.is_err() {
assert_cli::Assert::main_binary()
.with_args(&[dir_s])
.stdout()
.contains(r2)
.unwrap();
}
}
#[cfg(target_os = "macos")]
fn hard_link_output(dir_s: &str, file_path_s: &str, link_name_s: &str) -> (String, String) {
let r = format!( let r = format!(
"{} "{}
{}", {}",
format_string(dir_s, true, " 4.0K", ""), format_string(dir_s, true, true, " 4.0K", "─┬"),
format_string(file_path_s, true, " 4.0K", "└──") format_string(file_path_s, true, true, " 4.0K", " └──")
); );
let r2 = format!(
"{}
{}",
format_string(dir_s, true, true, " 4.0K", "─┬"),
format_string(link_name_s, true, true, " 4.0K", " └──")
);
(r, r2)
}
assert_cli::Assert::main_binary() #[cfg(target_os = "linux")]
.with_args(&[dir_s]) fn hard_link_output(dir_s: &str, file_path_s: &str, link_name_s: &str) -> (String, String) {
.stdout() let r = format!(
.contains(r) "{}
.unwrap(); {}",
format_string(dir_s, true, true, " 8.0K", "─┬"),
format_string(file_path_s, true, true, " 4.0K", " └──")
);
let r2 = format!(
"{}
{}",
format_string(dir_s, true, true, " 8.0K", "─┬"),
format_string(link_name_s, true, true, " 4.0K", " └──")
);
(r, r2)
} }
//Check we don't recurse down an infinite symlink tree //Check we don't recurse down an infinite symlink tree
@@ -121,20 +247,36 @@ pub fn test_recursive_sym_link() {
let link_name = dir.path().join("the_link"); let link_name = dir.path().join("the_link");
let link_name_s = link_name.to_str().unwrap(); let link_name_s = link_name.to_str().unwrap();
let c = Command::new("cd").arg(dir_s).output();
assert!(c.is_ok());
let c = Command::new("ln") let c = Command::new("ln")
.arg("-s") .arg("-s")
.arg(".") .arg(dir_s)
.arg(link_name_s) .arg(link_name_s)
.output(); .output();
assert!(c.is_ok()); assert!(c.is_ok());
let r = format!("{}", format_string(dir_s, true, " 4.0K", ""));
assert_cli::Assert::main_binary() assert_cli::Assert::main_binary()
.with_args(&[dir_s]) .with_args(&[dir_s])
.stdout() .stdout()
.contains(r) .contains(recursive_sym_link_output(dir_s, link_name_s))
.unwrap(); .unwrap();
} }
#[cfg(target_os = "macos")]
fn recursive_sym_link_output(dir: &str, link_name: &str) -> String {
format!(
"{}
{}",
format_string(dir, true, true, " 4.0K", "─┬"),
format_string(link_name, true, true, " 4.0K", " └──",),
)
}
#[cfg(target_os = "linux")]
fn recursive_sym_link_output(dir: &str, link_name: &str) -> String {
format!(
"{}
{}",
format_string(dir, true, true, " 4.0K", "─┬"),
format_string(link_name, true, true, " 0B", " └──",),
)
}
+121 -79
View File
@@ -1,113 +1,155 @@
use std::cmp::Ordering;
use std::collections::HashMap;
use std::collections::HashSet; use std::collections::HashSet;
use std::fs::{self, ReadDir}; use walkdir::WalkDir;
use std::io;
use dust::Node;
mod platform; mod platform;
use self::platform::*; use self::platform::*;
pub fn get_dir_tree(filenames: &Vec<&str>, apparent_size: bool) -> (bool, Vec<Node>) { pub fn simplify_dir_names(filenames: Vec<&str>) -> HashSet<String> {
let mut permissions = true; let mut top_level_names: HashSet<String> = HashSet::new();
let mut results = vec![];
for &b in filenames { for t in filenames {
let mut new_name = String::from(b); let top_level_name = strip_end_slashes(t);
while new_name.chars().last() == Some('/') && new_name.len() != 1 { let mut can_add = true;
new_name.pop(); let mut to_remove: Vec<String> = Vec::new();
for tt in top_level_names.iter() {
let temp = tt.to_string();
if top_level_name.starts_with(&temp) {
can_add = false;
} else if tt.starts_with(&top_level_name) {
to_remove.push(temp);
}
}
for tr in to_remove {
top_level_names.remove(&tr);
}
if can_add {
top_level_names.insert(top_level_name);
} }
let (hp, data) = examine_dir_str(&new_name, apparent_size);
permissions = permissions && hp;
results.push(data);
} }
(permissions, results)
top_level_names
} }
fn examine_dir_str(loc: &str, apparent_size: bool) -> (bool, Node) { pub fn get_dir_tree(
top_level_names: HashSet<String>,
apparent_size: bool,
) -> (bool, HashMap<String, u64>, HashSet<String>) {
let mut permissions = 0;
let mut inodes: HashSet<(u64, u64)> = HashSet::new(); let mut inodes: HashSet<(u64, u64)> = HashSet::new();
let (hp, result) = examine_dir(fs::read_dir(loc), apparent_size, &mut inodes); let mut data: HashMap<String, u64> = HashMap::new();
// This needs to be folded into the below recursive call somehow for b in top_level_names.iter() {
let new_size = result.iter().fold(0, |a, b| a + b.size()); examine_dir(&b, apparent_size, &mut inodes, &mut data, &mut permissions);
(hp, Node::new(loc, new_size, result)) }
(permissions == 0, data, top_level_names)
}
fn strip_end_slashes(s: &str) -> String {
let mut new_name = String::from(s);
while new_name.chars().last() == Some('/') && new_name.len() != 1 {
new_name.pop();
}
new_name
} }
fn examine_dir( fn examine_dir(
a_dir: io::Result<ReadDir>, top_dir: &String,
apparent_size: bool, apparent_size: bool,
inodes: &mut HashSet<(u64, u64)>, inodes: &mut HashSet<(u64, u64)>,
) -> (bool, Vec<Node>) { data: &mut HashMap<String, u64>,
let mut result = vec![]; permissions: &mut u64,
let mut have_permission = true; ) {
for entry in WalkDir::new(top_dir) {
match entry {
Ok(e) => {
let maybe_size_and_inode = get_metadata(&e, apparent_size);
if a_dir.is_ok() { match maybe_size_and_inode {
let paths = a_dir.unwrap(); Some((size, maybe_inode)) => {
for dd in paths { if !apparent_size {
match dd { if let Some(inode_dev_pair) = maybe_inode {
Ok(d) => { if inodes.contains(&inode_dev_pair) {
let file_type = d.file_type().ok(); continue;
let maybe_size_and_inode = get_metadata(&d, apparent_size);
match (file_type, maybe_size_and_inode) {
(Some(file_type), Some((size, inode))) => {
let s = d.path().to_string_lossy().to_string();
if !apparent_size {
if let Some(inode_dev_pair) = inode {
if inodes.contains(&inode_dev_pair) {
continue;
}
inodes.insert(inode_dev_pair);
} }
} inodes.insert(inode_dev_pair);
if d.path().is_dir() && !file_type.is_symlink() {
let (hp, recursive) =
examine_dir(fs::read_dir(d.path()), apparent_size, inodes);
have_permission = have_permission && hp;
let new_size = recursive.iter().fold(size, |a, b| a + b.size());
result.push(Node::new(s, new_size, recursive))
} else {
result.push(Node::new(s, size, vec![]))
} }
} }
(_, None) => have_permission = false, let mut e_path = e.path().to_path_buf();
(_, _) => (), loop {
let path_name = e_path.to_string_lossy().to_string();
let s = data.entry(path_name.clone()).or_insert(0);
*s += size;
if path_name == *top_dir {
break;
}
e_path.pop();
}
} }
None => *permissions += 1,
} }
Err(_) => (),
} }
_ => {}
} }
} else {
have_permission = false;
} }
(have_permission, result) }
pub fn compare_tuple(a: &(String, u64), b: &(String, u64)) -> Ordering {
let result = b.1.cmp(&a.1);
if result == Ordering::Equal {
a.0.cmp(&b.0)
} else {
result
}
} }
// We start with a list of root directories - these must be the biggest folders pub fn sort<'a>(data: HashMap<String, u64>) -> Vec<(String, u64)> {
// We then repeadedly merge in the children of the biggest directory - Each iteration let mut new_l: Vec<(String, u64)> = data.iter().map(|(a, b)| (a.clone(), *b)).collect();
// the next biggest directory's children are merged in. new_l.sort_by(|a, b| compare_tuple(&a, &b));
pub fn find_big_ones<'a>(l: &'a Vec<Node>, max_to_show: usize) -> Vec<&Node> { new_l
let mut new_l: Vec<&Node> = l.iter().map(|a| a).collect(); }
new_l.sort();
for processed_pointer in 0..max_to_show { pub fn find_big_ones<'a>(new_l: Vec<(String, u64)>, max_to_show: usize) -> Vec<(String, u64)> {
if new_l.len() == processed_pointer { if max_to_show > 0 && new_l.len() > max_to_show {
break;
}
// Must be a list of pointers into new_l otherwise b_list will go out of scope
// when it is deallocated
let mut b_list: Vec<&Node> = new_l[processed_pointer]
.children()
.iter()
.map(|a| a)
.collect();
new_l.extend(b_list);
new_l.sort();
}
if new_l.len() > max_to_show {
new_l[0..max_to_show + 1].to_vec() new_l[0..max_to_show + 1].to_vec()
} else { } else {
new_l new_l
} }
} }
mod tests {
#[allow(unused_imports)]
use super::*;
#[test]
fn test_simplify_dir() {
let mut correct = HashSet::new();
correct.insert("a".to_string());
assert!(simplify_dir_names(vec!["a"]) == correct);
}
#[test]
fn test_simplify_dir_rm_subdir() {
let mut correct = HashSet::new();
correct.insert("a/b".to_string());
assert!(simplify_dir_names(vec!["a/b", "a/b/c", "a/b/d/f"]) == correct);
}
#[test]
fn test_simplify_dir_duplicates() {
let mut correct = HashSet::new();
correct.insert("a/b".to_string());
correct.insert("c".to_string());
assert!(simplify_dir_names(vec!["a/b", "a/b//", "c", "c/"]) == correct);
}
#[test]
fn test_simplify_dir_rm_subdir_and_not_substrings() {
let mut correct = HashSet::new();
correct.insert("a/b".to_string());
correct.insert("c/a/b".to_string());
correct.insert("b".to_string());
assert!(simplify_dir_names(vec!["a/b", "c/a/b/", "b"]) == correct);
}
}
+13 -60
View File
@@ -1,4 +1,4 @@
use std; use walkdir::DirEntry;
fn get_block_size() -> u64 { fn get_block_size() -> u64 {
// All os specific implementations of MetatdataExt seem to define a block as 512 bytes // All os specific implementations of MetatdataExt seem to define a block as 512 bytes
@@ -6,67 +6,20 @@ fn get_block_size() -> u64 {
512 512
} }
#[cfg(target_os = "linux")] #[cfg(target_family = "unix")]
pub fn get_metadata( pub fn get_metadata(d: &DirEntry, use_apparent_size: bool) -> Option<(u64, Option<(u64, u64)>)> {
d: &std::fs::DirEntry,
use_apparent_size: bool,
) -> Option<(u64, Option<(u64, u64)>)> {
use std::os::linux::fs::MetadataExt;
match d.metadata().ok() {
Some(md) => {
let inode = Some((md.st_ino(), md.st_dev()));
if use_apparent_size {
Some((md.len(), inode))
} else {
Some((md.st_blocks() * get_block_size(), inode))
}
}
None => None,
}
}
#[cfg(target_os = "unix")]
pub fn get_metadata(
d: &std::fs::DirEntry,
use_apparent_size: bool,
) -> Option<(u64, Option<(u64, u64)>)> {
use std::os::unix::fs::MetadataExt; use std::os::unix::fs::MetadataExt;
match d.metadata().ok() { d.metadata().ok().map_or(None, |md| {
Some(md) => { let inode = Some((md.ino(), md.dev()));
let inode = Some((md.ino(), md.dev())); if use_apparent_size {
if use_apparent_size { Some((md.len(), inode))
Some((md.len(), inode)) } else {
} else { Some((md.blocks() * get_block_size(), inode))
Some((md.blocks() * get_block_size(), inode))
}
} }
None => None, })
}
} }
#[cfg(target_os = "macos")] #[cfg(not(target_family = "unix"))]
pub fn get_metadata( pub fn get_metadata(d: &DirEntry, _apparent: bool) -> Option<(u64, Option<(u64, u64)>)> {
d: &std::fs::DirEntry, d.metadata().ok().map_or(None, |md| Some((md.len(), None)))
use_apparent_size: bool,
) -> Option<(u64, Option<(u64, u64)>)> {
use std::os::macos::fs::MetadataExt;
match d.metadata().ok() {
Some(md) => {
let inode = Some((md.st_ino(), md.st_dev()));
if use_apparent_size {
Some((md.len(), inode))
} else {
Some((md.st_blocks() * get_block_size(), inode))
}
}
None => None,
}
}
#[cfg(not(any(target_os = "linux", target_os = "unix", target_os = "macos")))]
pub fn get_metadata(d: &std::fs::DirEntry, _apparent: bool) -> Option<(u64, Option<(u64, u64)>)> {
match d.metadata().ok() {
Some(md) => Some((md.len(), None)),
None => None,
}
} }