diff --git a/.github/workflows/CICD.yml b/.github/workflows/CICD.yml deleted file mode 100644 index d1d9824..0000000 --- a/.github/workflows/CICD.yml +++ /dev/null @@ -1,347 +0,0 @@ -name: CICD - -# spell-checker:ignore CICD CODECOV MSVC MacOS Peltoche SHAs buildable clippy esac fakeroot gnueabihf halium libssl mkdir musl popd printf pushd rustfmt softprops toolchain - -env: - PROJECT_NAME: dust - PROJECT_DESC: "du + rust = dust" - PROJECT_AUTH: "bootandy" - RUST_MIN_SRV: "1.31.0" - -on: [push, pull_request] - -jobs: - style: - name: Style - runs-on: ${{ matrix.job.os }} - strategy: - fail-fast: false - matrix: - job: - - { os: ubuntu-latest } - - { os: macos-latest } - - { os: windows-latest } - steps: - - uses: actions/checkout@v1 - - name: Initialize workflow variables - id: vars - shell: bash - run: | - # 'windows-latest' `cargo fmt` is bugged for this project (see reasons @ GH:rust-lang/rustfmt #3324, #3590, #3688 ; waiting for repair) - JOB_DO_FORMAT_TESTING="true" - 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:-/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: - toolchain: stable - override: true - profile: minimal # minimal component installation (ie, no documentation) - components: rustfmt, clippy - - name: "`fmt` testing" - if: steps.vars.outputs.JOB_DO_FORMAT_TESTING - uses: actions-rs/cargo@v1 - with: - command: fmt - args: --all -- --check - - name: "`clippy` testing" - if: success() || failure() # run regardless of prior step ("`fmt` testing") success/failure - uses: actions-rs/cargo@v1 - with: - command: clippy - args: ${{ matrix.job.cargo-options }} ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }} -- -D warnings - - min_version: - name: MinSRV # Minimum supported rust version - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v1 - - name: Install `rust` toolchain (v${{ env.RUST_MIN_SRV }}) - uses: actions-rs/toolchain@v1 - with: - toolchain: ${{ env.RUST_MIN_SRV }} - profile: minimal # minimal component installation (ie, no documentation) - - name: Test - uses: actions-rs/cargo@v1 - with: - command: test - - build: - name: Build - runs-on: ${{ matrix.job.os }} - strategy: - fail-fast: false - matrix: - job: - # { os, target, cargo-options, features, use-cross, toolchain } - - { - os: ubuntu-latest, - target: aarch64-unknown-linux-gnu, - use-cross: use-cross, - } - - { - os: ubuntu-latest, - target: aarch64-unknown-linux-musl, - use-cross: use-cross, - } - - { - os: ubuntu-latest, - target: arm-unknown-linux-gnueabihf, - use-cross: use-cross, - } - - { - os: ubuntu-latest, - target: i686-unknown-linux-gnu, - use-cross: use-cross, - } - - { - os: ubuntu-latest, - target: i686-unknown-linux-musl, - use-cross: use-cross, - } - - { - os: ubuntu-latest, - target: x86_64-unknown-linux-gnu, - use-cross: use-cross, - } - - { - os: ubuntu-latest, - target: x86_64-unknown-linux-musl, - use-cross: use-cross, - } - - { os: macos-latest, target: x86_64-apple-darwin } - - { os: windows-latest, target: i686-pc-windows-gnu } - - { os: windows-latest, target: i686-pc-windows-msvc } - - { os: windows-latest, target: x86_64-pc-windows-gnu } ## !maint: [rivy; 2020-01-21] may break due to rust bug; follow possible solution from GH:rust-lang/rust#47048 (refs: GH:rust-lang/rust#47048 , GH:rust-lang/rust#53454 , GH:bike-barn/hermit#172 ) - - { os: windows-latest, target: x86_64-pc-windows-msvc } - steps: - - uses: actions/checkout@v1 - - name: Install any prerequisites - shell: bash - run: | - case ${{ matrix.job.target }} in - arm-unknown-linux-gnueabihf) sudo apt-get -y update ; sudo apt-get -y install gcc-arm-linux-gnueabihf ;; - aarch64-unknown-linux-gnu) sudo apt-get -y update ; sudo apt-get -y install binutils-aarch64-linux-gnu ;; - esac - - name: Initialize workflow variables - id: vars - shell: bash - run: | - # toolchain - TOOLCHAIN="stable" ## default to "stable" toolchain - # * specify alternate TOOLCHAIN for *-pc-windows-gnu targets; gnu targets on Windows are broken for the standard *-pc-windows-msvc toolchain (refs: , , ) - case ${{ matrix.job.target }} in *-pc-windows-gnu) TOOLCHAIN="stable-${{ matrix.job.target }}" ;; esac; - # * use requested TOOLCHAIN if specified - if [ -n "${{ matrix.job.toolchain }}" ]; then TOOLCHAIN="${{ matrix.job.toolchain }}" ; fi - echo set-output name=TOOLCHAIN::${TOOLCHAIN} - echo ::set-output name=TOOLCHAIN::${TOOLCHAIN} - # staging directory - STAGING='_staging' - echo set-output name=STAGING::${STAGING} - echo ::set-output name=STAGING::${STAGING} - # determine EXE suffix - EXE_suffix="" ; case ${{ matrix.job.target }} in *-pc-windows-*) EXE_suffix=".exe" ;; esac; - echo set-output name=EXE_suffix::${EXE_suffix} - echo ::set-output name=EXE_suffix::${EXE_suffix} - # parse commit reference info - REF_NAME=${GITHUB_REF#refs/*/} - unset REF_BRANCH ; case ${GITHUB_REF} in refs/heads/*) REF_BRANCH=${GITHUB_REF#refs/heads/} ;; esac; - unset REF_TAG ; case ${GITHUB_REF} in refs/tags/*) REF_TAG=${GITHUB_REF#refs/tags/} ;; esac; - REF_SHAS=${GITHUB_SHA:0:8} - echo set-output name=REF_NAME::${REF_NAME} - echo set-output name=REF_BRANCH::${REF_BRANCH} - echo set-output name=REF_TAG::${REF_TAG} - echo set-output name=REF_SHAS::${REF_SHAS} - echo ::set-output name=REF_NAME::${REF_NAME} - echo ::set-output name=REF_BRANCH::${REF_BRANCH} - echo ::set-output name=REF_TAG::${REF_TAG} - echo ::set-output name=REF_SHAS::${REF_SHAS} - # parse target - unset TARGET_ARCH ; case ${{ matrix.job.target }} in arm-unknown-linux-gnueabihf) TARGET_ARCH=arm ;; aarch-*) TARGET_ARCH=aarch64 ;; i686-*) TARGET_ARCH=i686 ;; x86_64-*) TARGET_ARCH=x86_64 ;; esac; - echo set-output name=TARGET_ARCH::${TARGET_ARCH} - echo ::set-output name=TARGET_ARCH::${TARGET_ARCH} - unset TARGET_OS ; case ${{ matrix.job.target }} in *-linux-*) TARGET_OS=linux ;; *-apple-*) TARGET_OS=macos ;; *-windows-*) TARGET_OS=windows ;; esac; - echo set-output name=TARGET_OS::${TARGET_OS} - echo ::set-output name=TARGET_OS::${TARGET_OS} - # package name - PKG_suffix=".tar.gz" ; case ${{ matrix.job.target }} in *-pc-windows-*) PKG_suffix=".zip" ;; esac; - PKG_BASENAME=${PROJECT_NAME}-${REF_TAG:-$REF_SHAS}-${{ matrix.job.target }} - PKG_NAME=${PKG_BASENAME}${PKG_suffix} - echo set-output name=PKG_suffix::${PKG_suffix} - echo set-output name=PKG_BASENAME::${PKG_BASENAME} - echo set-output name=PKG_NAME::${PKG_NAME} - echo ::set-output name=PKG_suffix::${PKG_suffix} - echo ::set-output name=PKG_BASENAME::${PKG_BASENAME} - echo ::set-output name=PKG_NAME::${PKG_NAME} - # deployable tag? (ie, leading "vM" or "M"; M == version number) - unset DEPLOY ; if [[ $REF_TAG =~ ^[vV]?[0-9].* ]]; then DEPLOY='true' ; fi - echo set-output name=DEPLOY::${DEPLOY:-/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:-/false} - echo ::set-output name=CARGO_USE_CROSS::${CARGO_USE_CROSS} - # # * `arm` cannot be tested on ubuntu-* hosts (b/c testing is currently primarily done via comparison of target outputs with built-in outputs and the `arm` target is not executable on the host) - JOB_DO_TESTING="true" - case ${{ matrix.job.target }} in arm-*|aarch64-*) unset JOB_DO_TESTING ;; esac; - echo set-output name=JOB_DO_TESTING::${JOB_DO_TESTING:-/false} - echo ::set-output name=JOB_DO_TESTING::${JOB_DO_TESTING} - # # * test only binary for arm-type targets - unset CARGO_TEST_OPTIONS - unset CARGO_TEST_OPTIONS ; case ${{ matrix.job.target }} in arm-*|aarch64-*) CARGO_TEST_OPTIONS="--bin ${PROJECT_NAME}" ;; esac; - echo set-output name=CARGO_TEST_OPTIONS::${CARGO_TEST_OPTIONS} - echo ::set-output name=CARGO_TEST_OPTIONS::${CARGO_TEST_OPTIONS} - # * strip executable? - STRIP="strip" ; case ${{ matrix.job.target }} in arm-unknown-linux-gnueabihf) STRIP="arm-linux-gnueabihf-strip" ;; *-pc-windows-msvc) STRIP="" ;; aarch64-unknown-linux-gnu) STRIP="aarch64-linux-gnu-strip" ;; aarch64-unknown-linux-musl) STRIP="" ;;esac; - echo set-output name=STRIP::${STRIP} - echo ::set-output name=STRIP::${STRIP} - - name: Create all needed build/work directories - shell: bash - run: | - mkdir -p '${{ steps.vars.outputs.STAGING }}' - mkdir -p '${{ steps.vars.outputs.STAGING }}/${{ steps.vars.outputs.PKG_BASENAME }}' - - name: rust toolchain ~ install - uses: actions-rs/toolchain@v1 - with: - toolchain: ${{ steps.vars.outputs.TOOLCHAIN }} - target: ${{ matrix.job.target }} - override: true - profile: minimal # minimal component installation (ie, no documentation) - - name: Info - shell: bash - run: | - gcc --version || true - rustup -V - rustup toolchain list - rustup default - cargo -V - rustc -V - - name: Build - uses: actions-rs/cargo@v1 - with: - use-cross: ${{ steps.vars.outputs.CARGO_USE_CROSS }} - command: build - args: --release --target=${{ matrix.job.target }} ${{ matrix.job.cargo-options }} ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }} - - name: Install cargo-deb - uses: actions-rs/cargo@v1 - with: - command: install - args: cargo-deb - if: matrix.job.target == 'i686-unknown-linux-musl' || matrix.job.target == 'x86_64-unknown-linux-musl' - - name: Build deb - uses: actions-rs/cargo@v1 - with: - command: deb - args: --no-build --target=${{ matrix.job.target }} - if: matrix.job.target == 'i686-unknown-linux-musl' || matrix.job.target == 'x86_64-unknown-linux-musl' - - 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 }} ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }} - - name: Archive executable artifacts - uses: actions/upload-artifact@master - with: - name: ${{ env.PROJECT_NAME }}-${{ matrix.job.target }} - path: target/${{ matrix.job.target }}/release/${{ env.PROJECT_NAME }}${{ steps.vars.outputs.EXE_suffix }} - - name: Archive deb artifacts - uses: actions/upload-artifact@master - with: - name: ${{ env.PROJECT_NAME }}-${{ matrix.job.target }}.deb - path: target/${{ matrix.job.target }}/debian - if: matrix.job.target == 'i686-unknown-linux-musl' || matrix.job.target == 'x86_64-unknown-linux-musl' - - name: Package - shell: bash - run: | - # binary - cp 'target/${{ matrix.job.target }}/release/${{ env.PROJECT_NAME }}${{ steps.vars.outputs.EXE_suffix }}' '${{ steps.vars.outputs.STAGING }}/${{ steps.vars.outputs.PKG_BASENAME }}/' - # `strip` binary (if needed) - if [ -n "${{ steps.vars.outputs.STRIP }}" ]; then "${{ steps.vars.outputs.STRIP }}" '${{ steps.vars.outputs.STAGING }}/${{ steps.vars.outputs.PKG_BASENAME }}/${{ env.PROJECT_NAME }}${{ steps.vars.outputs.EXE_suffix }}' ; fi - # README and LICENSE - cp README.md '${{ steps.vars.outputs.STAGING }}/${{ steps.vars.outputs.PKG_BASENAME }}/' - cp LICENSE '${{ steps.vars.outputs.STAGING }}/${{ steps.vars.outputs.PKG_BASENAME }}/' - # base compressed package - pushd '${{ steps.vars.outputs.STAGING }}/' >/dev/null - case ${{ matrix.job.target }} in - *-pc-windows-*) 7z -y a '${{ steps.vars.outputs.PKG_NAME }}' '${{ steps.vars.outputs.PKG_BASENAME }}'/* | tail -2 ;; - *) tar czf '${{ steps.vars.outputs.PKG_NAME }}' '${{ steps.vars.outputs.PKG_BASENAME }}'/* ;; - esac; - popd >/dev/null - - name: Publish - uses: softprops/action-gh-release@v1 - if: steps.vars.outputs.DEPLOY - with: - files: | - ${{ steps.vars.outputs.STAGING }}/${{ steps.vars.outputs.PKG_NAME }} - target/${{ matrix.job.target }}/debian/*.deb - - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - ## fix! [rivy; 2020-22-01] `cargo tarpaulin` is unable to test this repo at the moment; alternate recipe or another testing framework? - # coverage: - # name: Code Coverage - # runs-on: ${{ matrix.job.os }} - # strategy: - # fail-fast: true - # matrix: - # # job: [ { os: ubuntu-latest }, { os: macos-latest }, { os: windows-latest } ] - # job: [ { os: ubuntu-latest } ] ## cargo-tarpaulin is currently only available on linux - # steps: - # - uses: actions/checkout@v1 - # # - name: Reattach HEAD ## may be needed for accurate code coverage info - # # run: git checkout ${{ github.head_ref }} - # - name: Initialize workflow variables - # id: vars - # shell: bash - # run: | - # # staging directory - # STAGING='_staging' - # echo set-output name=STAGING::${STAGING} - # echo ::set-output name=STAGING::${STAGING} - # # check for CODECOV_TOKEN availability (work-around for inaccessible 'secrets' object for 'if'; see ) - # unset HAS_CODECOV_TOKEN - # if [ -n $CODECOV_TOKEN ]; then HAS_CODECOV_TOKEN='true' ; fi - # echo set-output name=HAS_CODECOV_TOKEN::${HAS_CODECOV_TOKEN} - # echo ::set-output name=HAS_CODECOV_TOKEN::${HAS_CODECOV_TOKEN} - # env: - # CODECOV_TOKEN: "${{ secrets.CODECOV_TOKEN }}" - # - name: Create all needed build/work directories - # shell: bash - # run: | - # mkdir -p '${{ steps.vars.outputs.STAGING }}/work' - # - name: Install required packages - # run: | - # sudo apt-get -y install libssl-dev - # pushd '${{ steps.vars.outputs.STAGING }}/work' >/dev/null - # wget --no-verbose https://github.com/xd009642/tarpaulin/releases/download/0.9.3/cargo-tarpaulin-0.9.3-travis.tar.gz - # tar xf cargo-tarpaulin-0.9.3-travis.tar.gz - # cp cargo-tarpaulin "$(dirname -- "$(which cargo)")"/ - # popd >/dev/null - # - name: Generate coverage - # run: | - # cargo tarpaulin --out Xml - # - name: Upload coverage results (CodeCov.io) - # # CODECOV_TOKEN (aka, "Repository Upload Token" for REPO from CodeCov.io) ## set via REPO/Settings/Secrets - # # if: secrets.CODECOV_TOKEN (not supported {yet?}; see ) - # if: steps.vars.outputs.HAS_CODECOV_TOKEN - # run: | - # # CodeCov.io - # cargo tarpaulin --out Xml - # bash <(curl -s https://codecov.io/bash) - # env: - # CODECOV_TOKEN: "${{ secrets.CODECOV_TOKEN }}" diff --git a/.gitignore b/.gitignore index cf19f31..fa39063 100644 --- a/.gitignore +++ b/.gitignore @@ -1,12 +1,3 @@ -# Generated by Cargo -# will have compiled files and executables -/target/ -# These are backup files generated by rustfmt -**/*.rs.bk -*.swp -.vscode/* -*.idea/* - -#ignore macos files -.DS_Store \ No newline at end of file +.idea +.envrc \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 3f09e3a..39e3878 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -40,17 +40,11 @@ version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ - "hermit-abi 0.1.19", + "hermit-abi", "libc", "winapi", ] -[[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - [[package]] name = "bitflags" version = "1.3.2" @@ -81,135 +75,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -[[package]] -name = "clap" -version = "3.2.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71655c45cb9845d3270c9d6df84ebe72b4dad3c2ba3f7023ad47c144e4e473a5" -dependencies = [ - "atty", - "bitflags", - "clap_lex", - "indexmap", - "strsim", - "termcolor", - "textwrap", -] - -[[package]] -name = "clap_complete" -version = "3.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f7a2e0a962c45ce25afce14220bc24f9dade0a1787f185cecf96bfba7847cd8" -dependencies = [ - "clap", -] - -[[package]] -name = "clap_lex" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" -dependencies = [ - "os_str_bytes", -] - -[[package]] -name = "clap_mangen" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "105180c05a72388d5f5e4e4f6c79eecb92497bda749fa8f963a16647c5d5377f" -dependencies = [ - "clap", - "roff", -] - -[[package]] -name = "config-file" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df51e72c150781d2c7d4cbcb0b803277caaa80476786994a62961a8f1010dafb" -dependencies = [ - "serde", - "thiserror", - "toml", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" - -[[package]] -name = "crossbeam-channel" -version = "0.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf2b3e8478797446514c91ef04bafcb59faba183e621ad488df88983cc14128c" -dependencies = [ - "cfg-if", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-deque" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" -dependencies = [ - "cfg-if", - "crossbeam-epoch", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46bd5f3f85273295a9d14aedfb86f6aadbff6d8f5295c4a9edb08e819dcf5695" -dependencies = [ - "autocfg", - "cfg-if", - "crossbeam-utils", - "memoffset", - "scopeguard", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" -dependencies = [ - "cfg-if", -] - [[package]] name = "difflib" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" -[[package]] -name = "directories" -version = "4.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f51c5d4ddabd36886dd3e1438cb358cdcb0d7c499cb99cb4ac2e38e18b5cb210" -dependencies = [ - "dirs-sys", -] - -[[package]] -name = "dirs-sys" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" -dependencies = [ - "libc", - "redox_users", - "winapi", -] - [[package]] name = "doc-comment" version = "0.3.3" @@ -217,23 +88,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" [[package]] -name = "du-dust" +name = "dust_style_filetree_display" version = "0.8.5" dependencies = [ "ansi_term", "assert_cmd", "atty", - "clap", - "clap_complete", - "clap_mangen", - "config-file", - "directories", "lscolors", - "rayon", "regex", - "serde", "stfu8", - "sysinfo", "tempfile", "terminal_size", "thousands", @@ -277,23 +140,6 @@ dependencies = [ "instant", ] -[[package]] -name = "getrandom" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" -dependencies = [ - "cfg-if", - "libc", - "wasi", -] - -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" - [[package]] name = "hermit-abi" version = "0.1.19" @@ -303,25 +149,6 @@ dependencies = [ "libc", ] -[[package]] -name = "hermit-abi" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" -dependencies = [ - "libc", -] - -[[package]] -name = "indexmap" -version = "1.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" -dependencies = [ - "autocfg", - "hashbrown", -] - [[package]] name = "instant" version = "0.1.12" @@ -384,24 +211,6 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" -[[package]] -name = "memoffset" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" -dependencies = [ - "autocfg", -] - -[[package]] -name = "ntapi" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc51db7b362b205941f71232e56c625156eb9a929f8cf74a428fd5bc094a4afc" -dependencies = [ - "winapi", -] - [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -412,28 +221,12 @@ dependencies = [ "winapi", ] -[[package]] -name = "num_cpus" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" -dependencies = [ - "hermit-abi 0.2.6", - "libc", -] - [[package]] name = "once_cell" version = "1.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" -[[package]] -name = "os_str_bytes" -version = "6.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" - [[package]] name = "overload" version = "0.1.1" @@ -467,46 +260,6 @@ dependencies = [ "termtree", ] -[[package]] -name = "proc-macro2" -version = "1.0.52" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d0e1ae9e836cc3beddd63db0df682593d7e2d3d891ae8c9083d2113e1744224" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "rayon" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" -dependencies = [ - "either", - "rayon-core", -] - -[[package]] -name = "rayon-core" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" -dependencies = [ - "crossbeam-channel", - "crossbeam-deque", - "crossbeam-utils", - "num_cpus", -] - [[package]] name = "redox_syscall" version = "0.2.16" @@ -516,17 +269,6 @@ dependencies = [ "bitflags", ] -[[package]] -name = "redox_users" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" -dependencies = [ - "getrandom", - "redox_syscall", - "thiserror", -] - [[package]] name = "regex" version = "1.7.1" @@ -550,12 +292,6 @@ version = "0.6.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" -[[package]] -name = "roff" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b833d8d034ea094b1ea68aa6d5c740e0d04bad9d16568d08ba6f76823a114316" - [[package]] name = "rustix" version = "0.36.9" @@ -570,31 +306,11 @@ dependencies = [ "windows-sys 0.45.0", ] -[[package]] -name = "scopeguard" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" - [[package]] name = "serde" version = "1.0.156" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "314b5b092c0ade17c00142951e50ced110ec27cea304b1037c6969246c2469a4" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.156" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7e29c4601e36bcec74a223228dce795f4cd3616341a4af93520ca1a837c087d" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] [[package]] name = "stfu8" @@ -606,38 +322,6 @@ dependencies = [ "regex", ] -[[package]] -name = "strsim" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" - -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "sysinfo" -version = "0.27.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a902e9050fca0a5d6877550b769abd2bd1ce8c04634b941dbe2809735e1a1e33" -dependencies = [ - "cfg-if", - "core-foundation-sys", - "libc", - "ntapi", - "once_cell", - "rayon", - "winapi", -] - [[package]] name = "tempfile" version = "3.4.0" @@ -651,15 +335,6 @@ dependencies = [ "windows-sys 0.42.0", ] -[[package]] -name = "termcolor" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" -dependencies = [ - "winapi-util", -] - [[package]] name = "terminal_size" version = "0.2.5" @@ -676,53 +351,12 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95059e91184749cb66be6dc994f67f182b6d897cb3df74a5bf66b5e709295fd8" -[[package]] -name = "textwrap" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" - -[[package]] -name = "thiserror" -version = "1.0.39" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5ab016db510546d856297882807df8da66a16fb8c4101cb8b30054b0d5b2d9c" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.39" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5420d42e90af0c38c3290abcca25b9b3bdf379fc9f55c528f53a269d9c9a267e" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "thousands" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3bf63baf9f5039dadc247375c29eb13706706cfde997d0330d05aa63a77d8820" -[[package]] -name = "toml" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" -dependencies = [ - "serde", -] - -[[package]] -name = "unicode-ident" -version = "1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" - [[package]] name = "unicode-width" version = "0.1.10" @@ -738,12 +372,6 @@ dependencies = [ "libc", ] -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" - [[package]] name = "winapi" version = "0.3.9" diff --git a/Cargo.toml b/Cargo.toml index a00e8bf..76c668b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,26 +1,19 @@ [package] -name = "du-dust" -description = "A more intuitive version of du" +name = "dust_style_filetree_display" +description = "a rip off of dust's display, useful for applications that want dust's output style for some reason" version = "0.8.5" authors = ["bootandy ", "nebkor "] edition = "2021" readme = "README.md" -documentation = "https://github.com/bootandy/dust" -homepage = "https://github.com/bootandy/dust" -repository = "https://github.com/bootandy/dust" +#documentation = "https://github.com/bootandy/dust" +#homepage = "https://github.com/bootandy/dust" +#repository = "https://github.com/bootandy/dust" +repository = "https://git.emunest.net/rei-forks/dust_style_filetree_display" 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" - [profile.release] codegen-units = 1 lto = true @@ -29,18 +22,12 @@ strip = true [dependencies] ansi_term = "0.12" atty = "0.2.14" -clap = "3.2.17" lscolors = "0.13" terminal_size = "0.2" unicode-width = "0.1" -rayon = "1" thousands = "0.2" stfu8 = "0.2" regex = "1" -config-file = "0.2" -serde = { version = "1.0", features = ["derive"] } -directories = "4" -sysinfo = "0.27" [target.'cfg(windows)'.dependencies] winapi-util = "0.1" @@ -48,41 +35,3 @@ winapi-util = "0.1" [dev-dependencies] assert_cmd = "2" tempfile = "=3" - -[build-dependencies] -clap = "3.2.17" -clap_complete = "3.2.4" -clap_mangen = "0.1" - -[[test]] -name = "integration" -path = "tests/tests.rs" - -[package.metadata.binstall] -pkg-url = "{ repo }/releases/download/v{ version }/dust-v{ version }-{ target }{ archive-suffix }" -bin-dir = "dust-v{ version }-{ target }/{ bin }{ binary-ext }" - -[package.metadata.deb] -section = "utils" -assets = [ - [ - "target/release/dust", - "usr/bin/", - "755", - ], - [ - "LICENSE", - "usr/share/doc/du-dust/", - "644", - ], - [ - "README.md", - "usr/share/doc/du-dust/README", - "644", - ], -] -extended-description = """\ -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 one -'Did not have permissions message'. -""" diff --git a/LICENSE b/LICENCE similarity index 100% rename from LICENSE rename to LICENCE diff --git a/README.md b/README.md index 18c628b..337bce4 100644 --- a/README.md +++ b/README.md @@ -1,97 +1,11 @@ -[![Build Status](https://travis-ci.org/bootandy/dust.svg?branch=master)](https://travis-ci.org/bootandy/dust) +# dust_style_filetree_display -# Dust +This crate contains the display components from [`dust`](https://github.com/bootandy/dust). +They have been ripped off, almost verbatim, from there! +I have removed some of the things that aren't useful for a library user but this is a very quick and rough crude rip-out job β€” I haven't documented anything or finished taking effort to clean it up. -du + rust = dust. Like du but more intuitive. +The dust style is nice and could be useful in other projects, that want to be look the same as dust +(e.g. because they want to be familiar to dust users, in order to display size tree information +whilst not displaying *exactly* what is on the filesystem). -# Why - -Because I want an easy way to see where my disk is being used. - -# Demo - -![Example](media/snap.png) - -## Install - -#### Cargo Packaging status - -- `cargo install du-dust` - -#### 🍺 Homebrew (Mac OS) - -- `brew install dust` - -#### 🍺 Homebrew (Linux) - -- `brew tap tgotwig/linux-dust && brew install dust` - -#### [Pacstall](https://github.com/pacstall/pacstall) (Debian/Ubuntu) - -- `pacstall -I dust-bin` - -#### [deb-get](https://github.com/wimpysworld/deb-get) (Debian/Ubuntu) - -- `deb-get install du-dust` - -#### Windows: - -- Windows GNU version - works -- Windows MSVC - requires: [VCRUNTIME140.dll](https://docs.microsoft.com/en-gb/cpp/windows/latest-supported-vc-redist?view=msvc-170) - -#### Download - -- 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 one 'Did not have permissions message'. - -Dust will list a slightly-less-than-the-terminal-height number of the biggest subdirectories 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 subdirectories will be colored. - -The different colors on the bars: These represent the combined tree hierarchy & disk usage. The shades of grey are used to indicate which parent folder a subfolder belongs to. For instance, look at the above screenshot. `.steam` is a folder taking 44% of the space. From the `.steam` bar is a light grey line that goes up. All these folders are inside `.steam` so if you delete `.steam` all that stuff will be gone too. - -## Usage - -``` -Usage: dust -Usage: dust -Usage: dust -Usage: dust -p (full-path - Show fullpath of the subdirectories) -Usage: dust -s (apparent-size - shows the length of the file as opposed to the amount of disk space it uses) -Usage: dust -n 30 (Shows 30 directories instead of the default [default is terminal height]) -Usage: dust -d 3 (Shows 3 levels of subdirectories) -Usage: dust -D (Show only directories (eg dust -D)) -Usage: dust -F (Show only files - finds your largest files) -Usage: dust -r (reverse order of output) -Usage: dust -H (si print sizes in powers of 1000 instead of 1024) -Usage: dust -X ignore (ignore all files and directories with the name 'ignore') -Usage: dust -x (Only show directories on the same filesystem) -Usage: dust -b (Do not show percentages or draw ASCII bars) -Usage: dust -i (Do not show hidden files) -Usage: dust -c (No colors [monochrome]) -Usage: dust -f (Count files instead of diskspace) -Usage: dust -t (Group by filetype) -Usage: dust -z 10M (min-size, Only include files larger than 10M) -Usage: dust -e regex (Only include files matching this regex (eg dust -e "\.png$" would match png files)) -Usage: dust -v regex (Exclude files matching this regex (eg dust -v "\.png$" would ignore png files)) -Usage: dust -L (dereference-links - Treat sym links as directories and go into them) -Usage: dust -P (Disable the progress indicator) -Usage: dust -R (For screen readers. Removes bars/symbols. Adds new column: depth level. (May want to use -p for full path too)) -Usage: dust --skip-total (No total row will be displayed) -Usage: dust -z 4000000 (Exclude files below size 4MB) - -``` - -## Alternatives - -- [NCDU](https://dev.yorhel.nl/ncdu) -- [dutree](https://github.com/nachoparker/dutree) -- [dua](https://github.com/Byron/dua-cli/) -- [pdu](https://github.com/KSXGitHub/parallel-disk-usage) -- [dirstat-rs](https://github.com/scullionw/dirstat-rs) -- 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. +The `dust` project is licensed under the Apache 2.0 licence, so this crate will do the same. diff --git a/build.rs b/build.rs deleted file mode 100644 index 86e8f7c..0000000 --- a/build.rs +++ /dev/null @@ -1,27 +0,0 @@ -use clap_complete::{generate_to, shells::*}; -use clap_mangen::Man; -use std::fs::File; -use std::io::Error; -use std::path::Path; - -include!("src/cli.rs"); - -fn main() -> Result<(), Error> { - let outdir = "completions"; - let app_name = "dust"; - let mut cmd = build_cli(); - - generate_to(Bash, &mut cmd, app_name, outdir)?; - generate_to(Zsh, &mut cmd, app_name, outdir)?; - generate_to(Fish, &mut cmd, app_name, outdir)?; - generate_to(PowerShell, &mut cmd, app_name, outdir)?; - generate_to(Elvish, &mut cmd, app_name, outdir)?; - - let file = Path::new("man-page").join("dust.1"); - std::fs::create_dir_all("man-page")?; - let mut file = File::create(file)?; - - Man::new(cmd).render(&mut file)?; - - Ok(()) -} diff --git a/ci/before_deploy.ps1 b/ci/before_deploy.ps1 deleted file mode 100644 index 710a503..0000000 --- a/ci/before_deploy.ps1 +++ /dev/null @@ -1,23 +0,0 @@ -# This script takes care of packaging the build artifacts that will go in the -# release zipfile - -$SRC_DIR = $PWD.Path -$STAGE = [System.Guid]::NewGuid().ToString() - -Set-Location $ENV:Temp -New-Item -Type Directory -Name $STAGE -Set-Location $STAGE - -$ZIP = "$SRC_DIR\$($Env:CRATE_NAME)-$($Env:APPVEYOR_REPO_TAG_NAME)-$($Env:TARGET).zip" - -# TODO Update this to package the right artifacts -Copy-Item "$SRC_DIR\target\$($Env:TARGET)\release\dust" '.\' - -7z a "$ZIP" * - -Push-AppveyorArtifact "$ZIP" - -Remove-Item *.* -Force -Set-Location .. -Remove-Item $STAGE -Set-Location $SRC_DIR diff --git a/ci/before_deploy.sh b/ci/before_deploy.sh deleted file mode 100644 index ab4426d..0000000 --- a/ci/before_deploy.sh +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env bash -# This script takes care of building your crate and packaging it for release - -set -ex - -main() { - local src=$(pwd) \ - stage= - - case $TRAVIS_OS_NAME in - linux) - stage=$(mktemp -d) - ;; - osx) - stage=$(mktemp -d -t tmp) - ;; - esac - - test -f Cargo.lock || cargo generate-lockfile - - # TODO Update this to build the artifacts that matter to you - cross rustc --bin dust --target $TARGET --release -- -C lto - - # TODO Update this to package the right artifacts - cp target/$TARGET/release/dust $stage/ - - cd $stage - tar czf $src/$CRATE_NAME-$TRAVIS_TAG-$TARGET.tar.gz * - cd $src - - rm -rf $stage -} - -main diff --git a/ci/how2publish.txt b/ci/how2publish.txt deleted file mode 100644 index 57addb2..0000000 --- a/ci/how2publish.txt +++ /dev/null @@ -1,14 +0,0 @@ -# ----------- To do a release --------- -# Compare times of runs to check no drastic slow down: -# time target/release/dust ~/dev -# time dust ~dev - -# edit version in cargo.toml -# tag a commit and push (increment version in Cargo.toml first): -# git tag v0.4.5 -# git push origin v0.4.5 - -# cargo publish to put it in crates.io - -# To install locally [Do before pushing it] -#cargo install --path . diff --git a/ci/install.sh b/ci/install.sh deleted file mode 100644 index 317a401..0000000 --- a/ci/install.sh +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/env bash -set -ex - -main() { - local target= - if [ $TRAVIS_OS_NAME = linux ]; then - target=x86_64-unknown-linux-musl - sort=sort - else - target=x86_64-apple-darwin - sort=gsort # for `sort --sort-version`, from brew's coreutils. - fi - - # This fetches latest stable release - local tag=$(git ls-remote --tags --refs --exit-code https://github.com/japaric/cross \ - | cut -d/ -f3 \ - | grep -E '^v[0.1.0-9.]+$' \ - | $sort --version-sort \ - | tail -n1) - curl -LSfs https://japaric.github.io/trust/install.sh | \ - sh -s -- \ - --force \ - --git japaric/cross \ - --tag $tag \ - --target $target -} - -main diff --git a/ci/script.sh b/ci/script.sh deleted file mode 100644 index 686ec6d..0000000 --- a/ci/script.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env bash -# This script takes care of testing your crate - -set -ex - -# TODO This is the "test phase", tweak it as you see fit -main() { - cross build --target $TARGET - cross build --target $TARGET --release - - if [ ! -z $DISABLE_TESTS ]; then - return - fi - - cross test --target $TARGET - cross test --target $TARGET --release - - cross run --target $TARGET - cross run --target $TARGET --release -} - -# we don't run the "test phase" when doing deploys -if [ -z $TRAVIS_TAG ]; then - main -fi diff --git a/completions/_dust b/completions/_dust deleted file mode 100644 index 9e8be82..0000000 --- a/completions/_dust +++ /dev/null @@ -1,77 +0,0 @@ -#compdef dust - -autoload -U is-at-least - -_dust() { - typeset -A opt_args - typeset -a _arguments_options - local ret=1 - - if is-at-least 5.2; then - _arguments_options=(-s -S -C) - else - _arguments_options=(-s -C) - fi - - local context curcontext="$curcontext" state line - _arguments "${_arguments_options[@]}" \ -'-d+[Depth to show]: : ' \ -'--depth=[Depth to show]: : ' \ -'-n+[Number of lines of output to show. (Default is terminal_height - 10)]: : ' \ -'--number-of-lines=[Number of lines of output to show. (Default is terminal_height - 10)]: : ' \ -'*-X+[Exclude any file or directory with this name]: : ' \ -'*--ignore-directory=[Exclude any file or directory with this name]: : ' \ -'-z+[Minimum size file to include in output]: : ' \ -'--min-size=[Minimum size file to include in output]: : ' \ -'(-e --filter -t --file_types)*-v+[Exclude filepaths matching this regex. To ignore png files type: -v "\\.png$" ]: : ' \ -'(-e --filter -t --file_types)*--invert-filter=[Exclude filepaths matching this regex. To ignore png files type: -v "\\.png$" ]: : ' \ -'(-t --file_types)*-e+[Only include filepaths matching this regex. For png files type: -e "\\.png$" ]: : ' \ -'(-t --file_types)*--filter=[Only include filepaths matching this regex. For png files type: -e "\\.png$" ]: : ' \ -'-w+[Specify width of output overriding the auto detection of terminal width]: : ' \ -'--terminal_width=[Specify width of output overriding the auto detection of terminal width]: : ' \ -'-h[Print help information]' \ -'--help[Print help information]' \ -'-V[Print version information]' \ -'--version[Print version information]' \ -'-p[Subdirectories will not have their path shortened]' \ -'--full-paths[Subdirectories will not have their path shortened]' \ -'-L[dereference sym links - Treat sym links as directories and go into them]' \ -'--dereference-links[dereference sym links - Treat sym links as directories and go into them]' \ -'-x[Only count the files and directories on the same filesystem as the supplied directory]' \ -'--limit-filesystem[Only count the files and directories on the same filesystem as the supplied directory]' \ -'-s[Use file length instead of blocks]' \ -'--apparent-size[Use file length instead of blocks]' \ -'-r[Print tree upside down (biggest highest)]' \ -'--reverse[Print tree upside down (biggest highest)]' \ -'-c[No colors will be printed (Useful for commands like: watch)]' \ -'--no-colors[No colors will be printed (Useful for commands like: watch)]' \ -'-b[No percent bars or percentages will be displayed]' \ -'--no-percent-bars[No percent bars or percentages will be displayed]' \ -'-R[For screen readers. Removes bars. Adds new column: depth level (May want to use -p too for full path)]' \ -'--screen-reader[For screen readers. Removes bars. Adds new column: depth level (May want to use -p too for full path)]' \ -'--skip-total[No total row will be displayed]' \ -'-f[Directory '\''size'\'' is number of child files/dirs not disk size]' \ -'--filecount[Directory '\''size'\'' is number of child files/dirs not disk size]' \ -'-i[Do not display hidden files]' \ -'--ignore_hidden[Do not display hidden files]' \ -'(-d --depth -D --only-dir)-t[show only these file types]' \ -'(-d --depth -D --only-dir)--file_types[show only these file types]' \ -'-H[print sizes in powers of 1000 (e.g., 1.1G)]' \ -'--si[print sizes in powers of 1000 (e.g., 1.1G)]' \ -'-P[Disable the progress indication.]' \ -'--no-progress[Disable the progress indication.]' \ -'(-F --only-file -t --file_types)-D[Only directories will be displayed.]' \ -'(-F --only-file -t --file_types)--only-dir[Only directories will be displayed.]' \ -'(-D --only-dir)-F[Only files will be displayed. (Finds your largest files)]' \ -'(-D --only-dir)--only-file[Only files will be displayed. (Finds your largest files)]' \ -'*::inputs:' \ -&& ret=0 -} - -(( $+functions[_dust_commands] )) || -_dust_commands() { - local commands; commands=() - _describe -t commands 'dust commands' commands "$@" -} - -_dust "$@" diff --git a/completions/_dust.ps1 b/completions/_dust.ps1 deleted file mode 100644 index f87a5b9..0000000 --- a/completions/_dust.ps1 +++ /dev/null @@ -1,79 +0,0 @@ - -using namespace System.Management.Automation -using namespace System.Management.Automation.Language - -Register-ArgumentCompleter -Native -CommandName 'dust' -ScriptBlock { - param($wordToComplete, $commandAst, $cursorPosition) - - $commandElements = $commandAst.CommandElements - $command = @( - 'dust' - for ($i = 1; $i -lt $commandElements.Count; $i++) { - $element = $commandElements[$i] - if ($element -isnot [StringConstantExpressionAst] -or - $element.StringConstantType -ne [StringConstantType]::BareWord -or - $element.Value.StartsWith('-') -or - $element.Value -eq $wordToComplete) { - break - } - $element.Value - }) -join ';' - - $completions = @(switch ($command) { - 'dust' { - [CompletionResult]::new('-d', 'd', [CompletionResultType]::ParameterName, 'Depth to show') - [CompletionResult]::new('--depth', 'depth', [CompletionResultType]::ParameterName, 'Depth to show') - [CompletionResult]::new('-n', 'n', [CompletionResultType]::ParameterName, 'Number of lines of output to show. (Default is terminal_height - 10)') - [CompletionResult]::new('--number-of-lines', 'number-of-lines', [CompletionResultType]::ParameterName, 'Number of lines of output to show. (Default is terminal_height - 10)') - [CompletionResult]::new('-X', 'X', [CompletionResultType]::ParameterName, 'Exclude any file or directory with this name') - [CompletionResult]::new('--ignore-directory', 'ignore-directory', [CompletionResultType]::ParameterName, 'Exclude any file or directory with this name') - [CompletionResult]::new('-z', 'z', [CompletionResultType]::ParameterName, 'Minimum size file to include in output') - [CompletionResult]::new('--min-size', 'min-size', [CompletionResultType]::ParameterName, 'Minimum size file to include in output') - [CompletionResult]::new('-v', 'v', [CompletionResultType]::ParameterName, 'Exclude filepaths matching this regex. To ignore png files type: -v "\.png$" ') - [CompletionResult]::new('--invert-filter', 'invert-filter', [CompletionResultType]::ParameterName, 'Exclude filepaths matching this regex. To ignore png files type: -v "\.png$" ') - [CompletionResult]::new('-e', 'e', [CompletionResultType]::ParameterName, 'Only include filepaths matching this regex. For png files type: -e "\.png$" ') - [CompletionResult]::new('--filter', 'filter', [CompletionResultType]::ParameterName, 'Only include filepaths matching this regex. For png files type: -e "\.png$" ') - [CompletionResult]::new('-w', 'w', [CompletionResultType]::ParameterName, 'Specify width of output overriding the auto detection of terminal width') - [CompletionResult]::new('--terminal_width', 'terminal_width', [CompletionResultType]::ParameterName, 'Specify width of output overriding the auto detection of terminal width') - [CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Print help information') - [CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Print help information') - [CompletionResult]::new('-V', 'V', [CompletionResultType]::ParameterName, 'Print version information') - [CompletionResult]::new('--version', 'version', [CompletionResultType]::ParameterName, 'Print version information') - [CompletionResult]::new('-p', 'p', [CompletionResultType]::ParameterName, 'Subdirectories will not have their path shortened') - [CompletionResult]::new('--full-paths', 'full-paths', [CompletionResultType]::ParameterName, 'Subdirectories will not have their path shortened') - [CompletionResult]::new('-L', 'L', [CompletionResultType]::ParameterName, 'dereference sym links - Treat sym links as directories and go into them') - [CompletionResult]::new('--dereference-links', 'dereference-links', [CompletionResultType]::ParameterName, 'dereference sym links - Treat sym links as directories and go into them') - [CompletionResult]::new('-x', 'x', [CompletionResultType]::ParameterName, 'Only count the files and directories on the same filesystem as the supplied directory') - [CompletionResult]::new('--limit-filesystem', 'limit-filesystem', [CompletionResultType]::ParameterName, 'Only count the files and directories on the same filesystem as the supplied directory') - [CompletionResult]::new('-s', 's', [CompletionResultType]::ParameterName, 'Use file length instead of blocks') - [CompletionResult]::new('--apparent-size', 'apparent-size', [CompletionResultType]::ParameterName, 'Use file length instead of blocks') - [CompletionResult]::new('-r', 'r', [CompletionResultType]::ParameterName, 'Print tree upside down (biggest highest)') - [CompletionResult]::new('--reverse', 'reverse', [CompletionResultType]::ParameterName, 'Print tree upside down (biggest highest)') - [CompletionResult]::new('-c', 'c', [CompletionResultType]::ParameterName, 'No colors will be printed (Useful for commands like: watch)') - [CompletionResult]::new('--no-colors', 'no-colors', [CompletionResultType]::ParameterName, 'No colors will be printed (Useful for commands like: watch)') - [CompletionResult]::new('-b', 'b', [CompletionResultType]::ParameterName, 'No percent bars or percentages will be displayed') - [CompletionResult]::new('--no-percent-bars', 'no-percent-bars', [CompletionResultType]::ParameterName, 'No percent bars or percentages will be displayed') - [CompletionResult]::new('-R', 'R', [CompletionResultType]::ParameterName, 'For screen readers. Removes bars. Adds new column: depth level (May want to use -p too for full path)') - [CompletionResult]::new('--screen-reader', 'screen-reader', [CompletionResultType]::ParameterName, 'For screen readers. Removes bars. Adds new column: depth level (May want to use -p too for full path)') - [CompletionResult]::new('--skip-total', 'skip-total', [CompletionResultType]::ParameterName, 'No total row will be displayed') - [CompletionResult]::new('-f', 'f', [CompletionResultType]::ParameterName, 'Directory ''size'' is number of child files/dirs not disk size') - [CompletionResult]::new('--filecount', 'filecount', [CompletionResultType]::ParameterName, 'Directory ''size'' is number of child files/dirs not disk size') - [CompletionResult]::new('-i', 'i', [CompletionResultType]::ParameterName, 'Do not display hidden files') - [CompletionResult]::new('--ignore_hidden', 'ignore_hidden', [CompletionResultType]::ParameterName, 'Do not display hidden files') - [CompletionResult]::new('-t', 't', [CompletionResultType]::ParameterName, 'show only these file types') - [CompletionResult]::new('--file_types', 'file_types', [CompletionResultType]::ParameterName, 'show only these file types') - [CompletionResult]::new('-H', 'H', [CompletionResultType]::ParameterName, 'print sizes in powers of 1000 (e.g., 1.1G)') - [CompletionResult]::new('--si', 'si', [CompletionResultType]::ParameterName, 'print sizes in powers of 1000 (e.g., 1.1G)') - [CompletionResult]::new('-P', 'P', [CompletionResultType]::ParameterName, 'Disable the progress indication.') - [CompletionResult]::new('--no-progress', 'no-progress', [CompletionResultType]::ParameterName, 'Disable the progress indication.') - [CompletionResult]::new('-D', 'D', [CompletionResultType]::ParameterName, 'Only directories will be displayed.') - [CompletionResult]::new('--only-dir', 'only-dir', [CompletionResultType]::ParameterName, 'Only directories will be displayed.') - [CompletionResult]::new('-F', 'F', [CompletionResultType]::ParameterName, 'Only files will be displayed. (Finds your largest files)') - [CompletionResult]::new('--only-file', 'only-file', [CompletionResultType]::ParameterName, 'Only files will be displayed. (Finds your largest files)') - break - } - }) - - $completions.Where{ $_.CompletionText -like "$wordToComplete*" } | - Sort-Object -Property ListItemText -} diff --git a/completions/dust.bash b/completions/dust.bash deleted file mode 100644 index 83d7b65..0000000 --- a/completions/dust.bash +++ /dev/null @@ -1,94 +0,0 @@ -_dust() { - local i cur prev opts cmds - COMPREPLY=() - cur="${COMP_WORDS[COMP_CWORD]}" - prev="${COMP_WORDS[COMP_CWORD-1]}" - cmd="" - opts="" - - for i in ${COMP_WORDS[@]} - do - case "${i}" in - "$1") - cmd="dust" - ;; - *) - ;; - esac - done - - case "${cmd}" in - dust) - opts="-h -V -d -n -p -X -L -x -s -r -c -b -z -R -f -i -v -e -t -w -H -P -D -F --help --version --depth --number-of-lines --full-paths --ignore-directory --dereference-links --limit-filesystem --apparent-size --reverse --no-colors --no-percent-bars --min-size --screen-reader --skip-total --filecount --ignore_hidden --invert-filter --filter --file_types --terminal_width --si --no-progress --only-dir --only-file ..." - if [[ ${cur} == -* || ${COMP_CWORD} -eq 1 ]] ; then - COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) - return 0 - fi - case "${prev}" in - --depth) - COMPREPLY=($(compgen -f "${cur}")) - return 0 - ;; - -d) - COMPREPLY=($(compgen -f "${cur}")) - return 0 - ;; - --number-of-lines) - COMPREPLY=($(compgen -f "${cur}")) - return 0 - ;; - -n) - COMPREPLY=($(compgen -f "${cur}")) - return 0 - ;; - --ignore-directory) - COMPREPLY=($(compgen -f "${cur}")) - return 0 - ;; - -X) - COMPREPLY=($(compgen -f "${cur}")) - return 0 - ;; - --min-size) - COMPREPLY=($(compgen -f "${cur}")) - return 0 - ;; - -z) - COMPREPLY=($(compgen -f "${cur}")) - return 0 - ;; - --invert-filter) - COMPREPLY=($(compgen -f "${cur}")) - return 0 - ;; - -v) - COMPREPLY=($(compgen -f "${cur}")) - return 0 - ;; - --filter) - COMPREPLY=($(compgen -f "${cur}")) - return 0 - ;; - -e) - COMPREPLY=($(compgen -f "${cur}")) - return 0 - ;; - --terminal_width) - COMPREPLY=($(compgen -f "${cur}")) - return 0 - ;; - -w) - COMPREPLY=($(compgen -f "${cur}")) - return 0 - ;; - *) - COMPREPLY=() - ;; - esac - COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) - return 0 - ;; - esac -} - -complete -F _dust -o bashdefault -o default dust diff --git a/completions/dust.elv b/completions/dust.elv deleted file mode 100644 index a11c4a9..0000000 --- a/completions/dust.elv +++ /dev/null @@ -1,73 +0,0 @@ - -use builtin; -use str; - -set edit:completion:arg-completer[dust] = {|@words| - fn spaces {|n| - builtin:repeat $n ' ' | str:join '' - } - fn cand {|text desc| - edit:complex-candidate $text &display=$text' '(spaces (- 14 (wcswidth $text)))$desc - } - var command = 'dust' - for word $words[1..-1] { - if (str:has-prefix $word '-') { - break - } - set command = $command';'$word - } - var completions = [ - &'dust'= { - cand -d 'Depth to show' - cand --depth 'Depth to show' - cand -n 'Number of lines of output to show. (Default is terminal_height - 10)' - cand --number-of-lines 'Number of lines of output to show. (Default is terminal_height - 10)' - cand -X 'Exclude any file or directory with this name' - cand --ignore-directory 'Exclude any file or directory with this name' - cand -z 'Minimum size file to include in output' - cand --min-size 'Minimum size file to include in output' - cand -v 'Exclude filepaths matching this regex. To ignore png files type: -v "\.png$" ' - cand --invert-filter 'Exclude filepaths matching this regex. To ignore png files type: -v "\.png$" ' - cand -e 'Only include filepaths matching this regex. For png files type: -e "\.png$" ' - cand --filter 'Only include filepaths matching this regex. For png files type: -e "\.png$" ' - cand -w 'Specify width of output overriding the auto detection of terminal width' - cand --terminal_width 'Specify width of output overriding the auto detection of terminal width' - cand -h 'Print help information' - cand --help 'Print help information' - cand -V 'Print version information' - cand --version 'Print version information' - cand -p 'Subdirectories will not have their path shortened' - cand --full-paths 'Subdirectories will not have their path shortened' - cand -L 'dereference sym links - Treat sym links as directories and go into them' - cand --dereference-links 'dereference sym links - Treat sym links as directories and go into them' - cand -x 'Only count the files and directories on the same filesystem as the supplied directory' - cand --limit-filesystem 'Only count the files and directories on the same filesystem as the supplied directory' - cand -s 'Use file length instead of blocks' - cand --apparent-size 'Use file length instead of blocks' - cand -r 'Print tree upside down (biggest highest)' - cand --reverse 'Print tree upside down (biggest highest)' - cand -c 'No colors will be printed (Useful for commands like: watch)' - cand --no-colors 'No colors will be printed (Useful for commands like: watch)' - cand -b 'No percent bars or percentages will be displayed' - cand --no-percent-bars 'No percent bars or percentages will be displayed' - cand -R 'For screen readers. Removes bars. Adds new column: depth level (May want to use -p too for full path)' - cand --screen-reader 'For screen readers. Removes bars. Adds new column: depth level (May want to use -p too for full path)' - cand --skip-total 'No total row will be displayed' - cand -f 'Directory ''size'' is number of child files/dirs not disk size' - cand --filecount 'Directory ''size'' is number of child files/dirs not disk size' - cand -i 'Do not display hidden files' - cand --ignore_hidden 'Do not display hidden files' - cand -t 'show only these file types' - cand --file_types 'show only these file types' - cand -H 'print sizes in powers of 1000 (e.g., 1.1G)' - cand --si 'print sizes in powers of 1000 (e.g., 1.1G)' - cand -P 'Disable the progress indication.' - cand --no-progress 'Disable the progress indication.' - cand -D 'Only directories will be displayed.' - cand --only-dir 'Only directories will be displayed.' - cand -F 'Only files will be displayed. (Finds your largest files)' - cand --only-file 'Only files will be displayed. (Finds your largest files)' - } - ] - $completions[$command] -} diff --git a/completions/dust.fish b/completions/dust.fish deleted file mode 100644 index 526530f..0000000 --- a/completions/dust.fish +++ /dev/null @@ -1,25 +0,0 @@ -complete -c dust -s d -l depth -d 'Depth to show' -r -complete -c dust -s n -l number-of-lines -d 'Number of lines of output to show. (Default is terminal_height - 10)' -r -complete -c dust -s X -l ignore-directory -d 'Exclude any file or directory with this name' -r -complete -c dust -s z -l min-size -d 'Minimum size file to include in output' -r -complete -c dust -s v -l invert-filter -d 'Exclude filepaths matching this regex. To ignore png files type: -v "\\.png$" ' -r -complete -c dust -s e -l filter -d 'Only include filepaths matching this regex. For png files type: -e "\\.png$" ' -r -complete -c dust -s w -l terminal_width -d 'Specify width of output overriding the auto detection of terminal width' -r -complete -c dust -s h -l help -d 'Print help information' -complete -c dust -s V -l version -d 'Print version information' -complete -c dust -s p -l full-paths -d 'Subdirectories will not have their path shortened' -complete -c dust -s L -l dereference-links -d 'dereference sym links - Treat sym links as directories and go into them' -complete -c dust -s x -l limit-filesystem -d 'Only count the files and directories on the same filesystem as the supplied directory' -complete -c dust -s s -l apparent-size -d 'Use file length instead of blocks' -complete -c dust -s r -l reverse -d 'Print tree upside down (biggest highest)' -complete -c dust -s c -l no-colors -d 'No colors will be printed (Useful for commands like: watch)' -complete -c dust -s b -l no-percent-bars -d 'No percent bars or percentages will be displayed' -complete -c dust -s R -l screen-reader -d 'For screen readers. Removes bars. Adds new column: depth level (May want to use -p too for full path)' -complete -c dust -l skip-total -d 'No total row will be displayed' -complete -c dust -s f -l filecount -d 'Directory \'size\' is number of child files/dirs not disk size' -complete -c dust -s i -l ignore_hidden -d 'Do not display hidden files' -complete -c dust -s t -l file_types -d 'show only these file types' -complete -c dust -s H -l si -d 'print sizes in powers of 1000 (e.g., 1.1G)' -complete -c dust -s P -l no-progress -d 'Disable the progress indication.' -complete -c dust -s D -l only-dir -d 'Only directories will be displayed.' -complete -c dust -s F -l only-file -d 'Only files will be displayed. (Finds your largest files)' diff --git a/config/config.toml b/config/config.toml deleted file mode 100644 index 314cd2a..0000000 --- a/config/config.toml +++ /dev/null @@ -1,28 +0,0 @@ -# Sample Config file, works with toml and yaml -# Place in either: -# ~/.config/dust/config.toml -# ~/.dust.toml - -# Print tree upside down (biggest highest) -reverse=true - -# Subdirectories will not have their path shortened -display-full-paths=true - -# Use file length instead of blocks -display-apparent-size=true - -# No colors will be printed -no-colors=true - -# No percent bars or percentages will be displayed -no-bars=true - -# No total row will be displayed -skip-total=true - -# Do not display hidden files -ignore-hidden=true - -# print sizes in powers of 1000 (e.g., 1.1G) -iso=true diff --git a/man-page/dust.1 b/man-page/dust.1 deleted file mode 100644 index e05bfb1..0000000 --- a/man-page/dust.1 +++ /dev/null @@ -1,90 +0,0 @@ -.ie \n(.g .ds Aq \(aq -.el .ds Aq ' -.TH Dust 1 "Dust 0.8.5" -.SH NAME -Dust \- Like du but more intuitive -.SH SYNOPSIS -\fBDust\fR [\fB\-h\fR|\fB\-\-help\fR] [\fB\-V\fR|\fB\-\-version\fR] [\fB\-d\fR|\fB\-\-depth\fR] [\fB\-n\fR|\fB\-\-number\-of\-lines\fR] [\fB\-p\fR|\fB\-\-full\-paths\fR] [\fB\-X\fR|\fB\-\-ignore\-directory\fR] [\fB\-L\fR|\fB\-\-dereference\-links\fR] [\fB\-x\fR|\fB\-\-limit\-filesystem\fR] [\fB\-s\fR|\fB\-\-apparent\-size\fR] [\fB\-r\fR|\fB\-\-reverse\fR] [\fB\-c\fR|\fB\-\-no\-colors\fR] [\fB\-b\fR|\fB\-\-no\-percent\-bars\fR] [\fB\-z\fR|\fB\-\-min\-size\fR] [\fB\-R\fR|\fB\-\-screen\-reader\fR] [\fB\-\-skip\-total\fR] [\fB\-f\fR|\fB\-\-filecount\fR] [\fB\-i\fR|\fB\-\-ignore_hidden\fR] [\fB\-v\fR|\fB\-\-invert\-filter\fR] [\fB\-e\fR|\fB\-\-filter\fR] [\fB\-t\fR|\fB\-\-file_types\fR] [\fB\-w\fR|\fB\-\-terminal_width\fR] [\fB\-H\fR|\fB\-\-si\fR] [\fB\-P\fR|\fB\-\-no\-progress\fR] [\fB\-D\fR|\fB\-\-only\-dir\fR] [\fB\-F\fR|\fB\-\-only\-file\fR] [\fIinputs\fR] -.SH DESCRIPTION -Like du but more intuitive -.SH OPTIONS -.TP -\fB\-h\fR, \fB\-\-help\fR -Print help information -.TP -\fB\-V\fR, \fB\-\-version\fR -Print version information -.TP -\fB\-d\fR, \fB\-\-depth\fR -Depth to show -.TP -\fB\-n\fR, \fB\-\-number\-of\-lines\fR -Number of lines of output to show. (Default is terminal_height \- 10) -.TP -\fB\-p\fR, \fB\-\-full\-paths\fR -Subdirectories will not have their path shortened -.TP -\fB\-X\fR, \fB\-\-ignore\-directory\fR -Exclude any file or directory with this name -.TP -\fB\-L\fR, \fB\-\-dereference\-links\fR -dereference sym links \- Treat sym links as directories and go into them -.TP -\fB\-x\fR, \fB\-\-limit\-filesystem\fR -Only count the files and directories on the same filesystem as the supplied directory -.TP -\fB\-s\fR, \fB\-\-apparent\-size\fR -Use file length instead of blocks -.TP -\fB\-r\fR, \fB\-\-reverse\fR -Print tree upside down (biggest highest) -.TP -\fB\-c\fR, \fB\-\-no\-colors\fR -No colors will be printed (Useful for commands like: watch) -.TP -\fB\-b\fR, \fB\-\-no\-percent\-bars\fR -No percent bars or percentages will be displayed -.TP -\fB\-z\fR, \fB\-\-min\-size\fR -Minimum size file to include in output -.TP -\fB\-R\fR, \fB\-\-screen\-reader\fR -For screen readers. Removes bars. Adds new column: depth level (May want to use \-p too for full path) -.TP -\fB\-\-skip\-total\fR -No total row will be displayed -.TP -\fB\-f\fR, \fB\-\-filecount\fR -Directory \*(Aqsize\*(Aq is number of child files/dirs not disk size -.TP -\fB\-i\fR, \fB\-\-ignore_hidden\fR -Do not display hidden files -.TP -\fB\-v\fR, \fB\-\-invert\-filter\fR -Exclude filepaths matching this regex. To ignore png files type: \-v "\\.png$" -.TP -\fB\-e\fR, \fB\-\-filter\fR -Only include filepaths matching this regex. For png files type: \-e "\\.png$" -.TP -\fB\-t\fR, \fB\-\-file_types\fR -show only these file types -.TP -\fB\-w\fR, \fB\-\-terminal_width\fR -Specify width of output overriding the auto detection of terminal width -.TP -\fB\-H\fR, \fB\-\-si\fR -print sizes in powers of 1000 (e.g., 1.1G) -.TP -\fB\-P\fR, \fB\-\-no\-progress\fR -Disable the progress indication. -.TP -\fB\-D\fR, \fB\-\-only\-dir\fR -Only directories will be displayed. -.TP -\fB\-F\fR, \fB\-\-only\-file\fR -Only files will be displayed. (Finds your largest files) -.TP -[\fIinputs\fR] - -.SH VERSION -v0.8.5 diff --git a/media/snap.png b/media/snap.png deleted file mode 100644 index 0ee57a3..0000000 Binary files a/media/snap.png and /dev/null differ diff --git a/shell.nix b/shell.nix new file mode 100644 index 0000000..4261562 --- /dev/null +++ b/shell.nix @@ -0,0 +1,19 @@ +{ pkgs ? import {} }: + +let + # We may need some packages from nixpkgs-unstable + #unstable = import {}; + + rust-toolchain = pkgs.symlinkJoin { + name = "rust-toolchain"; + paths = [pkgs.rustc pkgs.cargo pkgs.clippy pkgs.rustfmt pkgs.rustPlatform.rustcSrc]; + }; +in + +pkgs.mkShell { + + buildInputs = [ + rust-toolchain + ]; + +} diff --git a/src/cli.rs b/src/cli.rs deleted file mode 100644 index 4d8ffd8..0000000 --- a/src/cli.rs +++ /dev/null @@ -1,172 +0,0 @@ -use clap::{Arg, Command}; - -// For single thread mode set this variable on your command line: -// export RAYON_NUM_THREADS=1 - -pub fn build_cli() -> Command<'static> { - Command::new("Dust") - .about("Like du but more intuitive") - .version(env!("CARGO_PKG_VERSION")) - .trailing_var_arg(true) - .arg( - Arg::new("depth") - .short('d') - .long("depth") - .help("Depth to show") - .takes_value(true) - ) - .arg( - Arg::new("number_of_lines") - .short('n') - .long("number-of-lines") - .help("Number of lines of output to show. (Default is terminal_height - 10)") - .takes_value(true) - ) - .arg( - Arg::new("display_full_paths") - .short('p') - .long("full-paths") - .help("Subdirectories will not have their path shortened"), - ) - .arg( - Arg::new("ignore_directory") - .short('X') - .long("ignore-directory") - .takes_value(true) - .number_of_values(1) - .multiple_occurrences(true) - .help("Exclude any file or directory with this name"), - ) - .arg( - Arg::new("dereference_links") - .short('L') - .long("dereference-links") - .help("dereference sym links - Treat sym links as directories and go into them"), - ) - .arg( - Arg::new("limit_filesystem") - .short('x') - .long("limit-filesystem") - .help("Only count the files and directories on the same filesystem as the supplied directory"), - ) - .arg( - Arg::new("display_apparent_size") - .short('s') - .long("apparent-size") - .help("Use file length instead of blocks"), - ) - .arg( - Arg::new("reverse") - .short('r') - .long("reverse") - .help("Print tree upside down (biggest highest)"), - ) - .arg( - Arg::new("no_colors") - .short('c') - .long("no-colors") - .help("No colors will be printed (Useful for commands like: watch)"), - ) - .arg( - Arg::new("no_bars") - .short('b') - .long("no-percent-bars") - .help("No percent bars or percentages will be displayed"), - ) - .arg( - Arg::new("min_size") - .short('z') - .long("min-size") - .takes_value(true) - .number_of_values(1) - .help("Minimum size file to include in output"), - ) - .arg( - Arg::new("screen_reader") - .short('R') - .long("screen-reader") - .help("For screen readers. Removes bars. Adds new column: depth level (May want to use -p too for full path)"), - ) - .arg( - Arg::new("skip_total") - .long("skip-total") - .help("No total row will be displayed"), - ) - .arg( - Arg::new("by_filecount") - .short('f') - .long("filecount") - .help("Directory 'size' is number of child files/dirs not disk size"), - ) - .arg( - Arg::new("ignore_hidden") - .short('i') // Do not use 'h' this is used by 'help' - .long("ignore_hidden") - .help("Do not display hidden files"), - ) - .arg( - Arg::new("invert_filter") - .short('v') - .long("invert-filter") - .takes_value(true) - .number_of_values(1) - .multiple_occurrences(true) - .conflicts_with("filter") - .conflicts_with("types") - .help("Exclude filepaths matching this regex. To ignore png files type: -v \"\\.png$\" "), - ) - .arg( - Arg::new("filter") - .short('e') - .long("filter") - .takes_value(true) - .number_of_values(1) - .multiple_occurrences(true) - .conflicts_with("types") - .help("Only include filepaths matching this regex. For png files type: -e \"\\.png$\" "), - ) - .arg( - Arg::new("types") - .short('t') - .long("file_types") - .conflicts_with("depth") - .conflicts_with("only_dir") - .help("show only these file types"), - ) - .arg( - Arg::new("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::new("iso") - .short('H') - .long("si") - .help("print sizes in powers of 1000 (e.g., 1.1G)") - ) - .arg( - Arg::new("disable_progress") - .short('P') - .long("no-progress") - .help("Disable the progress indication."), - ) - .arg( - Arg::new("only_dir") - .short('D') - .long("only-dir") - .conflicts_with("only_file") - .conflicts_with("types") - .help("Only directories will be displayed."), - ) - .arg( - Arg::new("only_file") - .short('F') - .long("only-file") - .conflicts_with("only_dir") - .help("Only files will be displayed. (Finds your largest files)"), - ) - .arg(Arg::new("inputs").multiple_occurrences(true)) -} diff --git a/src/config.rs b/src/config.rs deleted file mode 100644 index 9a77f52..0000000 --- a/src/config.rs +++ /dev/null @@ -1,215 +0,0 @@ -use clap::ArgMatches; -use config_file::FromConfigFile; -use serde::Deserialize; -use std::path::Path; -use std::path::PathBuf; - -use crate::display::UNITS; - -#[derive(Deserialize, Default)] -#[serde(rename_all = "kebab-case")] -#[serde(deny_unknown_fields)] -pub struct Config { - pub display_full_paths: Option, - pub display_apparent_size: Option, - pub reverse: Option, - pub no_colors: Option, - pub no_bars: Option, - pub skip_total: Option, - pub screen_reader: Option, - pub ignore_hidden: Option, - pub iso: Option, - pub min_size: Option, - pub only_dir: Option, - pub only_file: Option, - pub disable_progress: Option, - pub depth: Option, -} - -impl Config { - pub fn get_no_colors(&self, options: &ArgMatches) -> bool { - Some(true) == self.no_colors || options.is_present("no_colors") - } - pub fn get_disable_progress(&self, options: &ArgMatches) -> bool { - Some(true) == self.disable_progress || options.is_present("disable_progress") - } - pub fn get_apparent_size(&self, options: &ArgMatches) -> bool { - Some(true) == self.display_apparent_size || options.is_present("display_apparent_size") - } - pub fn get_ignore_hidden(&self, options: &ArgMatches) -> bool { - Some(true) == self.ignore_hidden || options.is_present("ignore_hidden") - } - pub fn get_full_paths(&self, options: &ArgMatches) -> bool { - // If we are only showing files, always show full paths - Some(true) == self.display_full_paths - || options.is_present("display_full_paths") - || self.get_only_file(options) - } - pub fn get_reverse(&self, options: &ArgMatches) -> bool { - Some(true) == self.reverse || options.is_present("reverse") - } - pub fn get_no_bars(&self, options: &ArgMatches) -> bool { - Some(true) == self.no_bars || options.is_present("no_bars") - } - pub fn get_iso(&self, options: &ArgMatches) -> bool { - Some(true) == self.iso || options.is_present("iso") - } - pub fn get_skip_total(&self, options: &ArgMatches) -> bool { - Some(true) == self.skip_total || options.is_present("skip_total") - } - pub fn get_screen_reader(&self, options: &ArgMatches) -> bool { - Some(true) == self.screen_reader || options.is_present("screen_reader") - } - pub fn get_depth(&self, options: &ArgMatches) -> usize { - if let Some(v) = options.value_of("depth") { - if let Ok(v) = v.parse::() { - return v; - } - } - - self.depth.unwrap_or(usize::MAX) - } - pub fn get_min_size(&self, options: &ArgMatches, iso: bool) -> Option { - let size_from_param = options.value_of("min_size"); - self._get_min_size(size_from_param, iso) - } - fn _get_min_size(&self, min_size: Option<&str>, iso: bool) -> Option { - let size_from_param = min_size.and_then(|a| convert_min_size(a, iso)); - - if size_from_param.is_none() { - self.min_size - .as_ref() - .and_then(|a| convert_min_size(a.as_ref(), iso)) - } else { - size_from_param - } - } - pub fn get_only_dir(&self, options: &ArgMatches) -> bool { - Some(true) == self.only_dir || options.is_present("only_dir") - } - pub fn get_only_file(&self, options: &ArgMatches) -> bool { - Some(true) == self.only_file || options.is_present("only_file") - } -} - -fn convert_min_size(input: &str, iso: bool) -> Option { - let chars_as_vec: Vec = input.chars().collect(); - match chars_as_vec.split_last() { - Some((last, start)) => { - let mut starts: String = start.iter().collect::(); - - for (i, u) in UNITS.iter().rev().enumerate() { - if Some(*u) == last.to_uppercase().next() { - return match starts.parse::() { - Ok(pure) => { - let num: usize = if iso { 1000 } else { 1024 }; - let marker = pure * num.pow((i + 1) as u32); - Some(marker) - } - Err(_) => { - eprintln!("Ignoring invalid min-size: {input}"); - None - } - }; - } - } - starts.push(*last); - starts - .parse() - .map_err(|_| { - eprintln!("Ignoring invalid min-size: {input}"); - }) - .ok() - } - None => None, - } -} - -fn get_config_locations(base: &Path) -> Vec { - vec![ - base.join(".dust.toml"), - base.join(".config").join("dust").join("config.toml"), - ] -} - -pub fn get_config() -> Config { - if let Some(home) = directories::BaseDirs::new() { - for path in get_config_locations(home.home_dir()) { - if path.exists() { - if let Ok(config) = Config::from_config_file(path) { - return config; - } - } - } - } - Config { - ..Default::default() - } -} - -#[cfg(test)] -mod tests { - #[allow(unused_imports)] - use super::*; - use clap::{Arg, ArgMatches, Command}; - - #[test] - fn test_conversion() { - assert_eq!(convert_min_size("55", false), Some(55)); - assert_eq!(convert_min_size("12344321", false), Some(12344321)); - assert_eq!(convert_min_size("95RUBBISH", false), None); - assert_eq!(convert_min_size("10K", false), Some(10 * 1024)); - assert_eq!(convert_min_size("10M", false), Some(10 * 1024usize.pow(2))); - assert_eq!(convert_min_size("10M", true), Some(10 * 1000usize.pow(2))); - assert_eq!(convert_min_size("2G", false), Some(2 * 1024usize.pow(3))); - } - - #[test] - fn test_min_size_from_config_applied_or_overridden() { - let c = Config { - min_size: Some("1K".to_owned()), - ..Default::default() - }; - assert_eq!(c._get_min_size(None, false), Some(1024)); - assert_eq!(c._get_min_size(Some("2K"), false), Some(2048)); - - assert_eq!(c._get_min_size(None, true), Some(1000)); - assert_eq!(c._get_min_size(Some("2K"), true), Some(2000)); - } - - #[test] - fn test_get_depth() { - // No config and no flag. - let c = Config::default(); - let args = get_args(vec![]); - assert_eq!(c.get_depth(&args), usize::MAX); - - // Config is not defined and flag is defined. - let c = Config::default(); - let args = get_args(vec!["dust", "--depth", "5"]); - assert_eq!(c.get_depth(&args), 5); - - // Config is defined and flag is not defined. - let c = Config { - depth: Some(3), - ..Default::default() - }; - let args = get_args(vec![]); - assert_eq!(c.get_depth(&args), 3); - - // Both config and flag are defined. - let c = Config { - depth: Some(3), - ..Default::default() - }; - let args = get_args(vec!["dust", "--depth", "5"]); - assert_eq!(c.get_depth(&args), 5); - } - - fn get_args(args: Vec<&str>) -> ArgMatches { - Command::new("Dust") - .trailing_var_arg(true) - .arg(Arg::new("depth").long("depth").takes_value(true)) - .get_matches_from(args) - } -} diff --git a/src/dir_walker.rs b/src/dir_walker.rs deleted file mode 100644 index 2863751..0000000 --- a/src/dir_walker.rs +++ /dev/null @@ -1,229 +0,0 @@ -use std::fs; -use std::sync::Arc; - -use crate::node::Node; -use crate::progress::Operation; -use crate::progress::PAtomicInfo; -use crate::progress::ORDERING; -use crate::utils::is_filtered_out_due_to_invert_regex; -use crate::utils::is_filtered_out_due_to_regex; -use rayon::iter::ParallelBridge; -use rayon::prelude::ParallelIterator; -use regex::Regex; -use std::path::PathBuf; - -use std::collections::HashSet; - -use crate::node::build_node; -use std::fs::DirEntry; - -use crate::platform::get_metadata; -pub struct WalkData<'a> { - pub ignore_directories: HashSet, - pub filter_regex: &'a [Regex], - pub invert_filter_regex: &'a [Regex], - pub allowed_filesystems: HashSet, - pub use_apparent_size: bool, - pub by_filecount: bool, - pub ignore_hidden: bool, - pub follow_links: bool, - pub progress_data: Arc, -} - -pub fn walk_it(dirs: HashSet, walk_data: WalkData) -> Vec { - let mut inodes = HashSet::new(); - let top_level_nodes: Vec<_> = dirs - .into_iter() - .filter_map(|d| { - let prog_data = &walk_data.progress_data; - prog_data.clear_state(&d); - let node = walk(d, &walk_data, 0)?; - - prog_data.state.store(Operation::PREPARING, ORDERING); - - clean_inodes(node, &mut inodes, walk_data.use_apparent_size) - }) - .collect(); - top_level_nodes -} - -// Remove files which have the same inode, we don't want to double count them. -fn clean_inodes( - x: Node, - inodes: &mut HashSet<(u64, u64)>, - use_apparent_size: bool, -) -> Option { - if !use_apparent_size { - if let Some(id) = x.inode_device { - if !inodes.insert(id) { - return None; - } - } - } - - // Sort Nodes so iteration order is predictable - let mut tmp: Vec<_> = x.children; - tmp.sort_by(sort_by_inode); - let new_children: Vec<_> = tmp - .into_iter() - .filter_map(|c| clean_inodes(c, inodes, use_apparent_size)) - .collect(); - - Some(Node { - name: x.name, - size: x.size + new_children.iter().map(|c| c.size).sum::(), - children: new_children, - inode_device: x.inode_device, - depth: x.depth, - }) -} - -fn sort_by_inode(a: &Node, b: &Node) -> std::cmp::Ordering { - // Sorting by inode is quicker than by sorting by name/size - if let Some(x) = a.inode_device { - if let Some(y) = b.inode_device { - if x.0 != y.0 { - return x.0.cmp(&y.0); - } else if x.1 != y.1 { - return x.1.cmp(&y.1); - } - } - } - a.name.cmp(&b.name) -} - -fn ignore_file(entry: &DirEntry, walk_data: &WalkData) -> bool { - let is_dot_file = entry.file_name().to_str().unwrap_or("").starts_with('.'); - let is_ignored_path = walk_data.ignore_directories.contains(&entry.path()); - - if !walk_data.allowed_filesystems.is_empty() { - let size_inode_device = get_metadata(&entry.path(), false); - - if let Some((_size, Some((_id, dev)))) = size_inode_device { - if !walk_data.allowed_filesystems.contains(&dev) { - return true; - } - } - } - - // Keeping `walk_data.filter_regex.is_empty()` is important for performance reasons, it stops unnecessary work - if !walk_data.filter_regex.is_empty() - && entry.path().is_file() - && is_filtered_out_due_to_regex(walk_data.filter_regex, &entry.path()) - { - return true; - } - - if !walk_data.invert_filter_regex.is_empty() - && entry.path().is_file() - && is_filtered_out_due_to_invert_regex(walk_data.invert_filter_regex, &entry.path()) - { - return true; - } - - (is_dot_file && walk_data.ignore_hidden) || is_ignored_path -} - -fn walk(dir: PathBuf, walk_data: &WalkData, depth: usize) -> Option { - let prog_data = &walk_data.progress_data; - let mut children = vec![]; - - if let Ok(entries) = fs::read_dir(&dir) { - children = entries - .into_iter() - .par_bridge() - .filter_map(|entry| { - if let Ok(ref entry) = entry { - // uncommenting the below line gives simpler code but - // rayon doesn't parallelize as well giving a 3X performance drop - // hence we unravel the recursion a bit - - // return walk(entry.path(), walk_data, depth) - - if !ignore_file(entry, walk_data) { - if let Ok(data) = entry.file_type() { - if data.is_dir() || (walk_data.follow_links && data.is_symlink()) { - return walk(entry.path(), walk_data, depth + 1); - } - - let node = build_node( - entry.path(), - vec![], - walk_data.filter_regex, - walk_data.invert_filter_regex, - walk_data.use_apparent_size, - data.is_symlink(), - data.is_file(), - walk_data.by_filecount, - depth, - ); - - prog_data.num_files.fetch_add(1, ORDERING); - if let Some(ref file) = node { - prog_data.total_file_size.fetch_add(file.size, ORDERING); - } - - return node; - } - } - } else { - prog_data.no_permissions.store(true, ORDERING) - } - None - }) - .collect(); - } else if !dir.is_file() { - walk_data.progress_data.no_permissions.store(true, ORDERING) - } - build_node( - dir, - children, - walk_data.filter_regex, - walk_data.invert_filter_regex, - walk_data.use_apparent_size, - false, - false, - walk_data.by_filecount, - depth, - ) -} - -mod tests { - #[allow(unused_imports)] - use super::*; - - #[cfg(test)] - fn create_node() -> Node { - Node { - name: PathBuf::new(), - size: 10, - children: vec![], - inode_device: Some((5, 6)), - depth: 0, - } - } - - #[test] - #[allow(clippy::redundant_clone)] - fn test_should_ignore_file() { - let mut inodes = HashSet::new(); - let n = create_node(); - - // First time we insert the node - assert_eq!(clean_inodes(n.clone(), &mut inodes, false), Some(n.clone())); - - // Second time is a duplicate - we ignore it - assert_eq!(clean_inodes(n.clone(), &mut inodes, false), None); - } - - #[test] - #[allow(clippy::redundant_clone)] - fn test_should_not_ignore_files_if_using_apparent_size() { - let mut inodes = HashSet::new(); - let n = create_node(); - - // If using apparent size we include Nodes, even if duplicate inodes - assert_eq!(clean_inodes(n.clone(), &mut inodes, true), Some(n.clone())); - assert_eq!(clean_inodes(n.clone(), &mut inodes, true), Some(n.clone())); - } -} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..b76da6f --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,178 @@ +pub mod display; +pub mod display_node; +pub mod filter; +pub mod filter_type; +pub mod node; +pub mod utils; + +use std::cmp::max; + +use terminal_size::{terminal_size, Height, Width}; + +static DEFAULT_NUMBER_OF_LINES: usize = 30; +static DEFAULT_TERMINAL_WIDTH: usize = 80; + +pub fn init_color(no_color: bool) -> bool { + #[cfg(windows)] + { + // 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 + } + } + } + } + #[cfg(not(windows))] + { + no_color + } +} + +pub fn get_height_of_terminal() -> usize { + // Simplify once https://github.com/eminence/terminal-size/pull/41 is + // merged + terminal_size() + // Windows CI runners detect a terminal height of 0 + .map(|(_, Height(h))| max(h as usize, DEFAULT_NUMBER_OF_LINES)) + .unwrap_or(DEFAULT_NUMBER_OF_LINES) + - 10 +} + +pub fn get_width_of_terminal() -> usize { + // Simplify once https://github.com/eminence/terminal-size/pull/41 is + // merged + terminal_size() + .map(|(Width(w), _)| match cfg!(windows) { + // Windows CI runners detect a very low terminal width + true => max(w as usize, DEFAULT_TERMINAL_WIDTH), + false => w as usize, + }) + .unwrap_or(DEFAULT_TERMINAL_WIDTH) +} + +// fn main() { +// let options = build_cli().get_matches(); +// let stdin_lines = get_lines_from_stdin(); +// +// let target_dirs = match options.values_of("inputs") { +// Some(values) => values.collect(), +// None => stdin_lines.as_ref().map_or(vec!["."], |lines| { +// lines.iter().map(String::as_str).collect() +// }), +// }; +// +// let summarize_file_types = options.is_present("types"); +// +// let filter_regexs = get_regex_value(options.values_of("filter")); +// let invert_filter_regexs = get_regex_value(options.values_of("invert_filter")); +// +// let terminal_width = options +// .value_of_t("width") +// .unwrap_or_else(|_| get_width_of_terminal()); +// +// // let depth = config.get_depth(&options); +// +// // If depth is set, then we set the default number_of_lines to be max +// // instead of screen height +// let default_height = if depth != usize::MAX { +// usize::MAX +// } else { +// get_height_of_terminal() +// }; +// +// let number_of_lines = options +// .value_of("number_of_lines") +// .and_then(|v| { +// v.parse() +// .map_err(|_| eprintln!("Ignoring bad value for number_of_lines")) +// .ok() +// }) +// .unwrap_or(default_height); +// +// // let no_colors = init_color(config.get_no_colors(&options)); +// +// let ignore_directories = options +// .values_of("ignore_directory") +// .unwrap_or_default() +// .map(PathBuf::from); +// +// let by_filecount = options.is_present("by_filecount"); +// let limit_filesystem = options.is_present("limit_filesystem"); +// let follow_links = options.is_present("dereference_links"); +// +// let simplified_dirs = simplify_dir_names(target_dirs); +// let allowed_filesystems = limit_filesystem +// .then(|| get_filesystem_devices(simplified_dirs.iter())) +// .unwrap_or_default(); +// +// let ignored_full_path: HashSet = ignore_directories +// .flat_map(|x| simplified_dirs.iter().map(move |d| d.join(&x))) +// .collect(); +// +// // let iso = config.get_iso(&options); +// +// // let ignore_hidden = config.get_ignore_hidden(&options); +// +// let mut indicator = PIndicator::build_me(); +// // if !config.get_disable_progress(&options) { +// // indicator.spawn(iso); +// // } +// +// let result = panic::catch_unwind(|| init_rayon); +// if result.is_err() { +// eprintln!("Problem initializing rayon, try: export RAYON_NUM_THREADS=1") +// } +// +// let top_level_nodes = walk_it(simplified_dirs, walk_data); +// +// let tree = match summarize_file_types { +// true => get_all_file_types(&top_level_nodes, number_of_lines), +// false => { +// let agg_data = AggregateData { +// min_size: config.get_min_size(&options, iso), +// only_dir: config.get_only_dir(&options), +// only_file: config.get_only_file(&options), +// number_of_lines, +// depth, +// using_a_filter: options.values_of("filter").is_some() +// || options.value_of("invert_filter").is_some(), +// }; +// get_biggest(top_level_nodes, agg_data) +// } +// }; +// +// let failed_permissions = indicator.data.no_permissions.load(ORDERING); +// indicator.stop(); +// // Must have stopped indicator before we print to stderr +// if failed_permissions { +// eprintln!("Did not have permissions for all directories"); +// } +// +// if let Some(root_node) = tree { +// let idd = InitialDisplayData { +// short_paths: !config.get_full_paths(&options), +// is_reversed: !config.get_reverse(&options), +// colors_on: !no_colors, +// by_filecount, +// iso, +// is_screen_reader: config.get_screen_reader(&options), +// }; +// draw_it( +// idd, +// config.get_no_bars(&options), +// terminal_width, +// &root_node, +// config.get_skip_total(&options), +// ) +// } +// } diff --git a/src/main.rs b/src/main.rs deleted file mode 100644 index fb1420b..0000000 --- a/src/main.rs +++ /dev/null @@ -1,257 +0,0 @@ -mod cli; -mod config; -mod dir_walker; -mod display; -mod display_node; -mod filter; -mod filter_type; -mod node; -mod platform; -mod progress; -mod utils; - -use crate::cli::build_cli; -use dir_walker::WalkData; -use display::InitialDisplayData; -use filter::AggregateData; -use progress::PIndicator; -use progress::ORDERING; -use std::collections::HashSet; -use std::io::BufRead; -use std::panic; -use std::process; -use sysinfo::{System, SystemExt}; - -use self::display::draw_it; -use clap::Values; -use config::get_config; -use dir_walker::walk_it; -use filter::get_biggest; -use filter_type::get_all_file_types; -use rayon::ThreadPoolBuildError; -use regex::Regex; -use std::cmp::max; -use std::path::PathBuf; -use terminal_size::{terminal_size, Height, Width}; -use utils::get_filesystem_devices; -use utils::simplify_dir_names; - -static DEFAULT_NUMBER_OF_LINES: usize = 30; -static DEFAULT_TERMINAL_WIDTH: usize = 80; - -fn init_color(no_color: bool) -> bool { - #[cfg(windows)] - { - // 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 - } - } - } - } - #[cfg(not(windows))] - { - no_color - } -} - -fn get_height_of_terminal() -> usize { - // Simplify once https://github.com/eminence/terminal-size/pull/41 is - // merged - terminal_size() - // Windows CI runners detect a terminal height of 0 - .map(|(_, Height(h))| max(h as usize, DEFAULT_NUMBER_OF_LINES)) - .unwrap_or(DEFAULT_NUMBER_OF_LINES) - - 10 -} - -fn get_width_of_terminal() -> usize { - // Simplify once https://github.com/eminence/terminal-size/pull/41 is - // merged - terminal_size() - .map(|(Width(w), _)| match cfg!(windows) { - // Windows CI runners detect a very low terminal width - true => max(w as usize, DEFAULT_TERMINAL_WIDTH), - false => w as usize, - }) - .unwrap_or(DEFAULT_TERMINAL_WIDTH) -} - -fn get_regex_value(maybe_value: Option) -> Vec { - maybe_value - .unwrap_or_default() - .map(|reg| { - Regex::new(reg).unwrap_or_else(|err| { - eprintln!("Ignoring bad value for regex {err:?}"); - process::exit(1) - }) - }) - .collect() -} - -// Returns a list of lines from stdin or `None` if there's nothing to read -fn get_lines_from_stdin() -> Option> { - atty::isnt(atty::Stream::Stdin).then(|| { - std::io::stdin() - .lock() - .lines() - .collect::>() - .expect("Error reading from stdin") - }) -} - -fn main() { - let options = build_cli().get_matches(); - let config = get_config(); - let stdin_lines = get_lines_from_stdin(); - - let target_dirs = match options.values_of("inputs") { - Some(values) => values.collect(), - None => stdin_lines.as_ref().map_or(vec!["."], |lines| { - lines.iter().map(String::as_str).collect() - }), - }; - - let summarize_file_types = options.is_present("types"); - - let filter_regexs = get_regex_value(options.values_of("filter")); - let invert_filter_regexs = get_regex_value(options.values_of("invert_filter")); - - let terminal_width = options - .value_of_t("width") - .unwrap_or_else(|_| get_width_of_terminal()); - - let depth = config.get_depth(&options); - - // If depth is set, then we set the default number_of_lines to be max - // instead of screen height - let default_height = if depth != usize::MAX { - usize::MAX - } else { - get_height_of_terminal() - }; - - let number_of_lines = options - .value_of("number_of_lines") - .and_then(|v| { - v.parse() - .map_err(|_| eprintln!("Ignoring bad value for number_of_lines")) - .ok() - }) - .unwrap_or(default_height); - - let no_colors = init_color(config.get_no_colors(&options)); - - let ignore_directories = options - .values_of("ignore_directory") - .unwrap_or_default() - .map(PathBuf::from); - - let by_filecount = options.is_present("by_filecount"); - let limit_filesystem = options.is_present("limit_filesystem"); - let follow_links = options.is_present("dereference_links"); - - let simplified_dirs = simplify_dir_names(target_dirs); - let allowed_filesystems = limit_filesystem - .then(|| get_filesystem_devices(simplified_dirs.iter())) - .unwrap_or_default(); - - let ignored_full_path: HashSet = ignore_directories - .flat_map(|x| simplified_dirs.iter().map(move |d| d.join(&x))) - .collect(); - - let iso = config.get_iso(&options); - - let ignore_hidden = config.get_ignore_hidden(&options); - - let mut indicator = PIndicator::build_me(); - if !config.get_disable_progress(&options) { - indicator.spawn(iso); - } - - let walk_data = WalkData { - ignore_directories: ignored_full_path, - filter_regex: &filter_regexs, - invert_filter_regex: &invert_filter_regexs, - allowed_filesystems, - use_apparent_size: config.get_apparent_size(&options), - by_filecount, - ignore_hidden, - follow_links, - progress_data: indicator.data.clone(), - }; - - let result = panic::catch_unwind(|| init_rayon); - if result.is_err() { - eprintln!("Problem initializing rayon, try: export RAYON_NUM_THREADS=1") - } - - let top_level_nodes = walk_it(simplified_dirs, walk_data); - - let tree = match summarize_file_types { - true => get_all_file_types(&top_level_nodes, number_of_lines), - false => { - let agg_data = AggregateData { - min_size: config.get_min_size(&options, iso), - only_dir: config.get_only_dir(&options), - only_file: config.get_only_file(&options), - number_of_lines, - depth, - using_a_filter: options.values_of("filter").is_some() - || options.value_of("invert_filter").is_some(), - }; - get_biggest(top_level_nodes, agg_data) - } - }; - - let failed_permissions = indicator.data.no_permissions.load(ORDERING); - indicator.stop(); - // Must have stopped indicator before we print to stderr - if failed_permissions { - eprintln!("Did not have permissions for all directories"); - } - - if let Some(root_node) = tree { - let idd = InitialDisplayData { - short_paths: !config.get_full_paths(&options), - is_reversed: !config.get_reverse(&options), - colors_on: !no_colors, - by_filecount, - iso, - is_screen_reader: config.get_screen_reader(&options), - }; - draw_it( - idd, - config.get_no_bars(&options), - terminal_width, - &root_node, - config.get_skip_total(&options), - ) - } -} - -fn init_rayon() -> Result<(), ThreadPoolBuildError> { - let large_stack = usize::pow(1024, 3); - let mut s = System::new(); - s.refresh_memory(); - let available = s.available_memory(); - - if available > large_stack.try_into().unwrap() { - // Larger stack size to handle cases with lots of nested directories - rayon::ThreadPoolBuilder::new() - .stack_size(large_stack) - .build_global() - } else { - rayon::ThreadPoolBuilder::new().build_global() - } -} diff --git a/src/node.rs b/src/node.rs index 78ecbf0..f799cde 100644 --- a/src/node.rs +++ b/src/node.rs @@ -1,8 +1,3 @@ -use crate::platform::get_metadata; -use crate::utils::is_filtered_out_due_to_invert_regex; -use crate::utils::is_filtered_out_due_to_regex; - -use regex::Regex; use std::cmp::Ordering; use std::path::PathBuf; @@ -15,47 +10,6 @@ pub struct Node { pub depth: usize, } -#[allow(clippy::too_many_arguments)] -pub fn build_node( - dir: PathBuf, - children: Vec, - filter_regex: &[Regex], - invert_filter_regex: &[Regex], - use_apparent_size: bool, - is_symlink: bool, - is_file: bool, - by_filecount: bool, - depth: usize, -) -> Option { - get_metadata(&dir, use_apparent_size).map(|data| { - let inode_device = if is_symlink && !use_apparent_size { - None - } else { - data.1 - }; - - let size = if is_filtered_out_due_to_regex(filter_regex, &dir) - || is_filtered_out_due_to_invert_regex(invert_filter_regex, &dir) - || (is_symlink && !use_apparent_size) - || by_filecount && !is_file - { - 0 - } else if by_filecount { - 1 - } else { - data.0 - }; - - Node { - name: dir, - size, - children, - inode_device, - depth, - } - }) -} - impl PartialEq for Node { fn eq(&self, other: &Self) -> bool { self.name == other.name && self.size == other.size && self.children == other.children diff --git a/src/platform.rs b/src/platform.rs deleted file mode 100644 index 77c578a..0000000 --- a/src/platform.rs +++ /dev/null @@ -1,128 +0,0 @@ -#[allow(unused_imports)] -use std::fs; - -use std::path::Path; - -#[cfg(target_family = "unix")] -fn get_block_size() -> u64 { - // All os specific implementations of MetadataExt seem to define a block as 512 bytes - // https://doc.rust-lang.org/std/os/linux/fs/trait.MetadataExt.html#tymethod.st_blocks - 512 -} - -#[cfg(target_family = "unix")] -pub fn get_metadata(d: &Path, use_apparent_size: bool) -> Option<(u64, Option<(u64, u64)>)> { - use std::os::unix::fs::MetadataExt; - 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")] -pub fn get_metadata(d: &Path, _use_apparent_size: bool) -> Option<(u64, Option<(u64, u64)>)> { - // On windows opening the file to get size, file ID and volume can be very - // expensive because 1) it causes a few system calls, and more importantly 2) it can cause - // windows defender to scan the file. - // Therefore we try to avoid doing that for common cases, mainly those of - // plain files: - - // The idea is to make do with the file size that we get from the OS for - // free as part of iterating a folder. Therefore we want to make sure that - // it makes sense to use that free size information: - - // Volume boundaries: - // The user can ask us not to cross volume boundaries. If the DirEntry is a - // plain file and not a reparse point or other non-trivial stuff, we assume - // that the file is located on the same volume as the directory that - // contains it. - - // File ID: - // This optimization does deprive us of access to a file ID. As a - // workaround, we just make one up that hopefully does not collide with real - // file IDs. - // Hard links: Unresolved. We don't get inode/file index, so hard links - // count once for each link. Hopefully they are not too commonly in use on - // windows. - - // Size: - // We assume (naively?) that for the common cases the free size info is the - // same as one would get by doing the expensive thing. Sparse, encrypted and - // compressed files are not included in the common cases, as one can image - // there being more than view on their size. - - // Savings in orders of magnitude in terms of time, io and cpu have been - // observed on hdd, windows 10, some 100Ks files taking up some hundreds of - // GBs: - // Consistently opening the file: 30 minutes. - // With this optimization: 8 sec. - - use std::io; - use winapi_util::Handle; - fn handle_from_path_limited>(path: P) -> io::Result { - use std::fs::OpenOptions; - use std::os::windows::fs::OpenOptionsExt; - const FILE_READ_ATTRIBUTES: u32 = 0x0080; - - // So, it seems that it does does have to be that expensive to open - // files to get their info: Avoiding opening the file with the full - // GENERIC_READ is key: - - // https://docs.microsoft.com/en-us/windows/win32/secauthz/generic-access-rights: - // "For example, a Windows file object maps the GENERIC_READ bit to the - // READ_CONTROL and SYNCHRONIZE standard access rights and to the - // FILE_READ_DATA, FILE_READ_EA, and FILE_READ_ATTRIBUTES - // object-specific access rights" - - // The flag FILE_READ_DATA seems to be the expensive one, so we'll avoid - // that, and a most of the other ones. Simply because it seems that we - // don't need them. - - let file = OpenOptions::new() - .access_mode(FILE_READ_ATTRIBUTES) - .open(path)?; - Ok(Handle::from_file(file)) - } - - fn get_metadata_expensive(d: &Path) -> Option<(u64, Option<(u64, u64)>)> { - use winapi_util::file::information; - - let h = handle_from_path_limited(d).ok()?; - let info = information(&h).ok()?; - - Some(( - info.file_size(), - Some((info.file_index(), info.volume_serial_number())), - )) - } - - use std::os::windows::fs::MetadataExt; - match d.metadata() { - Ok(ref md) => { - const FILE_ATTRIBUTE_ARCHIVE: u32 = 0x20; - const FILE_ATTRIBUTE_READONLY: u32 = 0x01; - const FILE_ATTRIBUTE_HIDDEN: u32 = 0x02; - const FILE_ATTRIBUTE_SYSTEM: u32 = 0x04; - const FILE_ATTRIBUTE_NORMAL: u32 = 0x80; - const FILE_ATTRIBUTE_DIRECTORY: u32 = 0x10; - - let attr_filtered = md.file_attributes() - & !(FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM); - if (attr_filtered & FILE_ATTRIBUTE_ARCHIVE) != 0 - || (attr_filtered & FILE_ATTRIBUTE_DIRECTORY) != 0 - || md.file_attributes() == FILE_ATTRIBUTE_NORMAL - { - Some((md.len(), None)) - } else { - get_metadata_expensive(d) - } - } - _ => get_metadata_expensive(d), - } -} diff --git a/src/utils.rs b/src/utils.rs index 30ea705..e688dad 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,8 +1,6 @@ -use platform::get_metadata; use std::collections::HashSet; use std::path::{Path, PathBuf}; -use crate::platform; use regex::Regex; pub fn simplify_dir_names>(filenames: Vec

) -> HashSet { @@ -31,17 +29,6 @@ pub fn simplify_dir_names>(filenames: Vec

) -> HashSet top_level_names } -pub fn get_filesystem_devices<'a, P: IntoIterator>(paths: P) -> HashSet { - // Gets the device ids for the filesystems which are used by the argument paths - paths - .into_iter() - .filter_map(|p| match get_metadata(p, false) { - Some((_size, Some((_id, dev)))) => Some(dev), - _ => None, - }) - .collect() -} - pub fn normalize_path>(path: P) -> PathBuf { // normalize path ... // 1. removing repeated separators diff --git a/tests/test_dir/many/a_file b/tests/test_dir/many/a_file deleted file mode 100644 index e69de29..0000000 diff --git a/tests/test_dir/many/hello_file b/tests/test_dir/many/hello_file deleted file mode 100644 index ce01362..0000000 --- a/tests/test_dir/many/hello_file +++ /dev/null @@ -1 +0,0 @@ -hello diff --git a/tests/test_dir2/dir/hello b/tests/test_dir2/dir/hello deleted file mode 100644 index b6fc4c6..0000000 --- a/tests/test_dir2/dir/hello +++ /dev/null @@ -1 +0,0 @@ -hello \ No newline at end of file diff --git a/tests/test_dir2/dir_name_clash b/tests/test_dir2/dir_name_clash deleted file mode 100644 index b6fc4c6..0000000 --- a/tests/test_dir2/dir_name_clash +++ /dev/null @@ -1 +0,0 @@ -hello \ No newline at end of file diff --git a/tests/test_dir2/dir_substring/hello b/tests/test_dir2/dir_substring/hello deleted file mode 100644 index ce01362..0000000 --- a/tests/test_dir2/dir_substring/hello +++ /dev/null @@ -1 +0,0 @@ -hello diff --git a/tests/test_dir2/long_dir_name_what_a_very_long_dir_name_what_happens_when_this_goes_over_80_characters_i_wonder b/tests/test_dir2/long_dir_name_what_a_very_long_dir_name_what_happens_when_this_goes_over_80_characters_i_wonder deleted file mode 100644 index e69de29..0000000 diff --git a/tests/test_dir_hidden_entries/.hidden_file b/tests/test_dir_hidden_entries/.hidden_file deleted file mode 100644 index 32f95c0..0000000 --- a/tests/test_dir_hidden_entries/.hidden_file +++ /dev/null @@ -1 +0,0 @@ -hi \ No newline at end of file diff --git a/tests/test_dir_unicode/γƒ©γ‚¦γƒˆγ―ι›£γ—γ„γ§γ™οΌ.japan b/tests/test_dir_unicode/γƒ©γ‚¦γƒˆγ―ι›£γ—γ„γ§γ™οΌ.japan deleted file mode 100644 index e69de29..0000000 diff --git a/tests/test_dir_unicode/πŸ‘©.unicode b/tests/test_dir_unicode/πŸ‘©.unicode deleted file mode 100644 index e69de29..0000000 diff --git a/tests/test_exact_output.rs b/tests/test_exact_output.rs deleted file mode 100644 index 4302c0b..0000000 --- a/tests/test_exact_output.rs +++ /dev/null @@ -1,208 +0,0 @@ -use assert_cmd::Command; -use std::ffi::OsStr; -use std::str; -use std::sync::Once; - -static INIT: Once = Once::new(); - -/** - * 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 in case it is there and has incorrect data - let last_slash = dir.rfind('/').unwrap(); - let last_part_of_dir = dir.chars().skip(last_slash).collect::(); - let _ = Command::new("rm") - .arg("-rf") - .arg("/tmp/".to_owned() + &*last_part_of_dir) - .ok(); - - let _ = Command::new("cp") - .arg("-r") - .arg(dir) - .arg("/tmp/") - .ok() - .map_err(|err| eprintln!("Error copying directory for test setup\n{:?}", err)); -} - -fn initialize() { - INIT.call_once(|| { - copy_test_data("tests/test_dir"); - copy_test_data("tests/test_dir2"); - copy_test_data("tests/test_dir_unicode"); - }); -} - -fn exact_output_test>(valid_outputs: Vec, command_args: Vec) { - initialize(); - - let mut a = &mut Command::cargo_bin("dust").unwrap(); - - for p in command_args { - a = a.arg(p); - } - - let output = str::from_utf8(&a.unwrap().stdout).unwrap().to_owned(); - - assert!(valid_outputs.iter().any(|i| output.contains(i))); -} - -// "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 - exact_output_test(main_output(), vec!["-c", "/tmp/test_dir/"]) -} - -#[cfg_attr(target_os = "windows", ignore)] -#[test] -pub fn test_main_multi_arg() { - let command_args = vec![ - "-c", - "/tmp/test_dir/many/", - "/tmp/test_dir", - "/tmp/test_dir", - ]; - exact_output_test(main_output(), command_args); -} - -fn main_output() -> Vec { - // Some linux currently thought to be Manjaro, Arch - // Although probably depends on how drive is formatted - let mac_and_some_linux = r#" - 0B β”Œβ”€β”€ a_file β”‚β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–ˆ β”‚ 0% -4.0K β”œβ”€β”€ hello_fileβ”‚β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ β”‚ 100% -4.0K β”Œβ”€β”΄ many β”‚β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ β”‚ 100% -4.0K β”Œβ”€β”΄ test_dir β”‚β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ β”‚ 100% -"# - .trim() - .to_string(); - - let ubuntu = r#" - 0B β”Œβ”€β”€ a_file β”‚ β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–ˆ β”‚ 0% -4.0K β”œβ”€β”€ hello_fileβ”‚ β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ β”‚ 33% -8.0K β”Œβ”€β”΄ many β”‚ β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ β”‚ 67% - 12K β”Œβ”€β”΄ test_dir β”‚β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ β”‚ 100% - "# - .trim() - .to_string(); - - vec![mac_and_some_linux, ubuntu] -} - -#[cfg_attr(target_os = "windows", ignore)] -#[test] -pub fn test_main_long_paths() { - let command_args = vec!["-c", "-p", "/tmp/test_dir/"]; - exact_output_test(main_output_long_paths(), command_args); -} - -fn main_output_long_paths() -> Vec { - let mac_and_some_linux = 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(); - let ubuntu = 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(); - vec![mac_and_some_linux, ubuntu] -} - -// Check against directories and files whose names are substrings of each other -#[cfg_attr(target_os = "windows", ignore)] -#[test] -pub fn test_substring_of_names_and_long_names() { - let command_args = vec!["-c", "/tmp/test_dir2"]; - exact_output_test(no_substring_of_names_output(), command_args); -} - -fn no_substring_of_names_output() -> Vec { - let ubuntu = " - 0B β”Œβ”€β”€ long_dir_name_what_a_very_long_dir_name_what_happens_when_this_goes.. -4.0K β”œβ”€β”€ dir_name_clash -4.0K β”‚ β”Œβ”€β”€ hello -8.0K β”œβ”€β”΄ dir -4.0K β”‚ β”Œβ”€β”€ hello -8.0K β”œβ”€β”΄ dir_substring - 24K β”Œβ”€β”΄ test_dir2 - " - .trim() - .into(); - - let mac_and_some_linux = " - 0B β”Œβ”€β”€ long_dir_name_what_a_very_long_dir_name_what_happens_when_this_goes.. -4.0K β”‚ β”Œβ”€β”€ hello -4.0K β”œβ”€β”΄ dir -4.0K β”œβ”€β”€ dir_name_clash -4.0K β”‚ β”Œβ”€β”€ hello -4.0K β”œβ”€β”΄ dir_substring - 12K β”Œβ”€β”΄ test_dir2 - " - .trim() - .into(); - vec![mac_and_some_linux, ubuntu] -} - -#[cfg_attr(target_os = "windows", ignore)] -#[test] -pub fn test_unicode_directories() { - let command_args = vec!["-c", "/tmp/test_dir_unicode"]; - exact_output_test(unicode_dir(), command_args); -} - -fn unicode_dir() -> Vec { - // The way unicode & asian characters are rendered on the terminal should make this line up - let ubuntu = " - 0B β”Œβ”€β”€ γƒ©γ‚¦γƒˆγ―ι›£γ—γ„γ§γ™οΌ.japanβ”‚ β–ˆ β”‚ 0% - 0B β”œβ”€β”€ πŸ‘©.unicode β”‚ β–ˆ β”‚ 0% -4.0K β”Œβ”€β”΄ test_dir_unicode β”‚β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ β”‚ 100% - " - .trim() - .into(); - - let mac_and_some_linux = " -0B β”Œβ”€β”€ γƒ©γ‚¦γƒˆγ―ι›£γ—γ„γ§γ™οΌ.japanβ”‚ β–ˆ β”‚ 0% -0B β”œβ”€β”€ πŸ‘©.unicode β”‚ β–ˆ β”‚ 0% -0B β”Œβ”€β”΄ test_dir_unicode β”‚ β–ˆ β”‚ 0% - " - .trim() - .into(); - vec![mac_and_some_linux, ubuntu] -} - -#[cfg_attr(target_os = "windows", ignore)] -#[test] -pub fn test_apparent_size() { - let command_args = vec!["-c", "-s", "-b", "/tmp/test_dir"]; - exact_output_test(apparent_size_output(), command_args); -} - -fn apparent_size_output() -> Vec { - // The apparent directory sizes are too unpredictable and system dependent to try and match - let files = r#" - 0B β”Œβ”€β”€ a_file - 6B β”œβ”€β”€ hello_file - "# - .trim() - .to_string(); - - vec![files] -} diff --git a/tests/test_flags.rs b/tests/test_flags.rs deleted file mode 100644 index 583f87c..0000000 --- a/tests/test_flags.rs +++ /dev/null @@ -1,222 +0,0 @@ -use assert_cmd::Command; -use std::ffi::OsStr; -use std::str; - -/** - * This file contains tests that test a substring of the output using '.contains' - * - * These tests should be the same cross platform - */ - -fn build_command>(command_args: Vec) -> String { - let mut cmd = &mut Command::cargo_bin("dust").unwrap(); - for p in command_args { - cmd = cmd.arg(p); - } - let finished = &cmd.unwrap(); - let stderr = str::from_utf8(&finished.stderr).unwrap(); - assert_eq!(stderr, ""); - - str::from_utf8(&finished.stdout).unwrap().into() -} - -// We can at least test the file names are there -#[test] -pub fn test_basic_output() { - let output = build_command(vec!["tests/test_dir/"]); - - 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 output = build_command(vec!["-b", "tests/test_dir/"]); - // 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 output = build_command(vec!["-r", "-c", "tests/test_dir/"]); - 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 output = build_command(vec!["-d", "1", "tests/test_dir/"]); - 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 output = build_command(vec!["-d", "1", "-f", "-c", "tests/test_dir2/"]); - assert!(output.contains("4 β”Œβ”€β”΄ test_dir2")); -} - -// Check against directories and files whose names are substrings of each other -#[test] -pub fn test_ignore_dir() { - let output = build_command(vec!["-c", "-X", "dir_substring", "tests/test_dir2/"]); - assert!(!output.contains("dir_substring")); -} -// Add test for multiple dirs - with -d 0 and maybe -d 1 check the - -#[test] -pub fn test_with_bad_param() { - let mut cmd = Command::cargo_bin("dust").unwrap(); - let result = cmd.arg("bad_place").unwrap(); - let stderr = str::from_utf8(&result.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 output = build_command(vec!["-c", "tests/test_dir_hidden_entries/"]); - 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 output = build_command(vec!["-c", "-i", "tests/test_dir_hidden_entries/"]); - 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 output = build_command(vec!["-c", "-f", "tests/test_dir"]); - assert!(output.contains("1 β”Œβ”€β”€ a_file ")); - assert!(output.contains("1 β”œβ”€β”€ hello_file")); - assert!(output.contains("2 β”Œβ”€β”΄ many")); - assert!(output.contains("2 β”Œβ”€β”΄ test_dir")); -} - -#[test] -pub fn test_show_files_by_type() { - // Check we can list files by type - let output = build_command(vec!["-c", "-t", "tests"]); - assert!(output.contains(" .unicode")); - assert!(output.contains(" .japan")); - assert!(output.contains(" .rs")); - assert!(output.contains(" (no extension)")); - assert!(output.contains("β”Œβ”€β”΄ (total)")); -} - -#[test] -#[cfg(target_family = "unix")] -pub fn test_show_files_only() { - let output = build_command(vec!["-c", "-F", "tests/test_dir"]); - assert!(output.contains("tests/test_dir/many/a_file")); - assert!(output.contains("tests/test_dir/many/hello_file")); - assert!(!output.contains("tests/test_dir/many ")); -} - -#[test] -pub fn test_output_skip_total() { - let output = build_command(vec![ - "--skip-total", - "tests/test_dir/many/hello_file", - "tests/test_dir/many/a_file", - ]); - assert!(output.contains("hello_file")); - assert!(!output.contains("(total)")); -} - -#[test] -pub fn test_output_screen_reader() { - let output = build_command(vec!["--screen-reader", "-c", "tests/test_dir/"]); - println!("{}", output); - assert!(output.contains("test_dir 0")); - assert!(output.contains("many 1")); - assert!(output.contains("hello_file 2")); - assert!(output.contains("a_file 2")); - - // Verify no 'symbols' reported by screen reader - assert!(!output.contains('β”‚')); - - for block in ['β–ˆ', 'β–“', 'β–’', 'β–‘'] { - assert!(!output.contains(block)); - } -} - -#[test] -pub fn test_show_files_by_regex_match_lots() { - // Check we can see '.rs' files in the tests directory - let output = build_command(vec!["-c", "-e", "\\.rs$", "tests"]); - assert!(output.contains(" β”Œβ”€β”΄ tests")); - assert!(!output.contains("0B β”Œβ”€β”€ tests")); - assert!(!output.contains("0B β”Œβ”€β”΄ tests")); -} - -#[test] -pub fn test_show_files_by_regex_match_nothing() { - // Check there are no files named: '.match_nothing' in the tests directory - let output = build_command(vec!["-c", "-e", "match_nothing$", "tests"]); - assert!(output.contains("0B β”Œβ”€β”€ tests")); -} - -#[test] -pub fn test_show_files_by_regex_match_multiple() { - let output = build_command(vec![ - "-c", - "-e", - "test_dir_hidden", - "-e", - "test_dir2", - "-n", - "100", - "tests", - ]); - assert!(output.contains("test_dir2")); - assert!(output.contains("test_dir_hidden")); - assert!(!output.contains("many")); // We do not find the 'many' folder in the 'test_dir' folder -} - -#[test] -pub fn test_show_files_by_invert_regex() { - let output = build_command(vec!["-c", "-f", "-v", "e", "tests/test_dir2"]); - // There are 0 files without 'e' in the name - assert!(output.contains("0 β”Œβ”€β”€ test_dir2")); - - let output = build_command(vec!["-c", "-f", "-v", "a", "tests/test_dir2"]); - // There are 2 files without 'a' in the name - assert!(output.contains("2 β”Œβ”€β”΄ test_dir2")); - - // There are 4 files in the test_dir2 hierarchy - let output = build_command(vec!["-c", "-f", "-v", "match_nothing$", "tests/test_dir2"]); - assert!(output.contains("4 β”Œβ”€β”΄ test_dir2")); -} - -#[test] -pub fn test_show_files_by_invert_regex_match_multiple() { - // We ignore test_dir2 & test_dir_unicode, leaving the test_dir folder - // which has the 'many' folder inside - let output = build_command(vec![ - "-c", - "-v", - "test_dir2", - "-v", - "test_dir_unicode", - "-n", - "100", - "tests", - ]); - assert!(!output.contains("test_dir2")); - assert!(!output.contains("test_dir_unicode")); - assert!(output.contains("many")); -} diff --git a/tests/tests.rs b/tests/tests.rs deleted file mode 100644 index 8b13789..0000000 --- a/tests/tests.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/tests/tests_symlinks.rs b/tests/tests_symlinks.rs deleted file mode 100644 index 55f1c3f..0000000 --- a/tests/tests_symlinks.rs +++ /dev/null @@ -1,141 +0,0 @@ -use assert_cmd::Command; -use std::fs::File; -use std::io::Write; -use std::path::PathBuf; -use std::str; - -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 build_temp_file(dir: &TempDir) -> PathBuf { - let file_path = dir.path().join("notes.txt"); - let mut file = File::create(&file_path).unwrap(); - writeln!(file, "I am a temp file").unwrap(); - file_path -} - -fn link_it(link_path: PathBuf, file_path_s: &str, is_soft: bool) -> String { - let link_name_s = link_path.to_str().unwrap(); - let mut c = Command::new("ln"); - if is_soft { - c.arg("-s"); - } - c.arg(file_path_s); - c.arg(link_name_s); - assert!(c.output().is_ok()); - link_name_s.into() -} - -#[cfg_attr(target_os = "windows", ignore)] -#[test] -pub fn test_soft_sym_link() { - let dir = Builder::new().tempdir().unwrap(); - let file = build_temp_file(&dir); - let dir_s = dir.path().to_str().unwrap(); - let file_path_s = file.to_str().unwrap(); - - let link_name = dir.path().join("the_link"); - let link_name_s = link_it(link_name, file_path_s, true); - - let c = format!(" β”œβ”€β”€ {}", link_name_s); - let b = format!(" β”Œβ”€β”€ {}", file_path_s); - let a = format!("─┴ {}", dir_s); - - let mut cmd = Command::cargo_bin("dust").unwrap(); - // Mac test runners create long filenames in tmp directories - let output = cmd - .args(["-p", "-c", "-s", "-w", "999", 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)] -#[test] -pub fn test_hard_sym_link() { - let dir = Builder::new().tempdir().unwrap(); - let file = build_temp_file(&dir); - let dir_s = dir.path().to_str().unwrap(); - let file_path_s = file.to_str().unwrap(); - - let link_name = dir.path().join("the_link"); - link_it(link_name, file_path_s, false); - - let file_output = format!(" β”Œβ”€β”€ {}", file_path_s); - let dirs_output = format!("─┴ {}", dir_s); - - let mut cmd = Command::cargo_bin("dust").unwrap(); - // Mac test runners create long filenames in tmp directories - let output = cmd.args(["-p", "-c", "-w", "999", dir_s]).unwrap().stdout; - - // The link should not appear in the output because multiple inodes are now ordered - // then filtered. - let output = str::from_utf8(&output).unwrap(); - assert!(output.contains(dirs_output.as_str())); - assert!(output.contains(file_output.as_str())); -} - -#[cfg_attr(target_os = "windows", ignore)] -#[test] -pub fn test_hard_sym_link_no_dup_multi_arg() { - let dir = Builder::new().tempdir().unwrap(); - let dir_link = Builder::new().tempdir().unwrap(); - let file = build_temp_file(&dir); - let dir_s = dir.path().to_str().unwrap(); - let dir_link_s = dir_link.path().to_str().unwrap(); - let file_path_s = file.to_str().unwrap(); - - let link_name = dir_link.path().join("the_link"); - let link_name_s = link_it(link_name, file_path_s, false); - - let mut cmd = Command::cargo_bin("dust").unwrap(); - - // Mac test runners create long filenames in tmp directories - let output = cmd - .args(["-p", "-c", "-w", "999", "-b", dir_link_s, dir_s]) - .unwrap() - .stdout; - - // The link or the file should appear but not both - let output = str::from_utf8(&output).unwrap(); - let has_file_only = output.contains(file_path_s) && !output.contains(&link_name_s); - let has_link_only = !output.contains(file_path_s) && output.contains(&link_name_s); - assert!(has_file_only || has_link_only); -} - -#[cfg_attr(target_os = "windows", ignore)] -#[test] -pub fn test_recursive_sym_link() { - let dir = Builder::new().tempdir().unwrap(); - let dir_s = dir.path().to_str().unwrap(); - - let link_name = dir.path().join("the_link"); - let link_name_s = link_it(link_name, dir_s, true); - - let a = format!("─┬ {}", dir_s); - let b = format!(" └── {}", link_name_s); - - let mut cmd = Command::cargo_bin("dust").unwrap(); - let output = cmd - .arg("-p") - .arg("-c") - .arg("-r") - .arg("-s") - .arg("-w") - .arg("999") - .arg(dir_s) - .unwrap() - .stdout; - let output = str::from_utf8(&output).unwrap(); - - assert!(output.contains(a.as_str())); - assert!(output.contains(b.as_str())); -}