mirror of
https://github.com/bootandy/dust.git
synced 2025-12-12 07:40:40 -08:00
Compare commits
172 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9452049aff | ||
|
|
09e3c27e70 | ||
|
|
b0bfe654c4 | ||
|
|
6bc44de495 | ||
|
|
efb455c739 | ||
|
|
2c58041885 | ||
|
|
c30f31c22c | ||
|
|
59f2cdfb84 | ||
|
|
795d91237d | ||
|
|
26ae050f16 | ||
|
|
58b395e7ee | ||
|
|
3beb2b5274 | ||
|
|
19d938b05e | ||
|
|
d4daa82297 | ||
|
|
21097578d9 | ||
|
|
069dc184a9 | ||
|
|
02fa657128 | ||
|
|
bc0e376c88 | ||
|
|
d7c602a2d2 | ||
|
|
603e6be7eb | ||
|
|
efa469e12f | ||
|
|
657858df16 | ||
|
|
a8d700d530 | ||
|
|
c408d8887d | ||
|
|
be2250d241 | ||
|
|
b6aa1378de | ||
|
|
2082141dfc | ||
|
|
8a9b5e889d | ||
|
|
871b7e90d8 | ||
|
|
edf300893c | ||
|
|
a3d8fc00e1 | ||
|
|
9d4531d48b | ||
|
|
75e419e7ed | ||
|
|
4a62fc5726 | ||
|
|
d2b959fdcf | ||
|
|
36ebb1b2b0 | ||
|
|
e126a01096 | ||
|
|
7a38a26593 | ||
|
|
1af6e1f757 | ||
|
|
affafcc5f2 | ||
|
|
26ef8c3e59 | ||
|
|
0898ee6bf0 | ||
|
|
5ac168868e | ||
|
|
684994ee11 | ||
|
|
416ad517fe | ||
|
|
7c34389aea | ||
|
|
18510130d8 | ||
|
|
0bf8c914b7 | ||
|
|
95888d5f31 | ||
|
|
5d195e27cb | ||
|
|
0c7b05fec9 | ||
|
|
bc895879e6 | ||
|
|
31f01c061a | ||
|
|
17894e723c | ||
|
|
6141ddddea | ||
|
|
352c0f7d90 | ||
|
|
842a8ec673 | ||
|
|
d97edba041 | ||
|
|
f64e0094f1 | ||
|
|
3d2477e554 | ||
|
|
350d695f7c | ||
|
|
f8b8b8a788 | ||
|
|
b9c27f9838 | ||
|
|
5541df6a73 | ||
|
|
bdc3d404ef | ||
|
|
f395a7d768 | ||
|
|
a36eec6cae | ||
|
|
38938e005e | ||
|
|
da61b15715 | ||
|
|
0b22d0a977 | ||
|
|
356d14ac0f | ||
|
|
311bc45388 | ||
|
|
ef66fb3938 | ||
|
|
b934445e04 | ||
|
|
a6839c020f | ||
|
|
7e47d5b47a | ||
|
|
6a65570f3f | ||
|
|
a4ca78dbe4 | ||
|
|
bfbe8a57ae | ||
|
|
b4b73e45f3 | ||
|
|
78119aba0f | ||
|
|
5535478fe8 | ||
|
|
7ba91a4a22 | ||
|
|
79416fd5fc | ||
|
|
b66523cff3 | ||
|
|
19a41aa382 | ||
|
|
62ac9b623a | ||
|
|
bf28d42483 | ||
|
|
f8ce6c97bf | ||
|
|
86b3cccaf6 | ||
|
|
3c920431fa | ||
|
|
a1ece05af5 | ||
|
|
cef2c588b7 | ||
|
|
7d8e498238 | ||
|
|
53c7a69dcb | ||
|
|
9a9cbefd3d | ||
|
|
224a2c6f25 | ||
|
|
99003cbba9 | ||
|
|
c83803b440 | ||
|
|
a41862d799 | ||
|
|
6ab46d8471 | ||
|
|
c727eb2d11 | ||
|
|
0effaa7fd7 | ||
|
|
25c50f88c4 | ||
|
|
0c19a66432 | ||
|
|
4cffc4370b | ||
|
|
db6c8a019d | ||
|
|
e03094a4fa | ||
|
|
1d9a56e025 | ||
|
|
ec2d9e19d4 | ||
|
|
9fbfcb275a | ||
|
|
1c60d1e2ac | ||
|
|
fd35734a94 | ||
|
|
c6f4ace2b6 | ||
|
|
d46b63fad8 | ||
|
|
872a49bb7d | ||
|
|
04c6c204c3 | ||
|
|
7ac01e8166 | ||
|
|
2f7a88e8dc | ||
|
|
2ca2cebdad | ||
|
|
80338f4731 | ||
|
|
d327bd2e68 | ||
|
|
7db6cf2f32 | ||
|
|
76d0762c97 | ||
|
|
6e03dd77e6 | ||
|
|
4906e9efda | ||
|
|
876609f2cb | ||
|
|
12775db94b | ||
|
|
bfaf5ee173 | ||
|
|
fd68330815 | ||
|
|
0bf4ebf554 | ||
|
|
cab24f58d5 | ||
|
|
b1b933d851 | ||
|
|
6a86e8befd | ||
|
|
3fb91a6c29 | ||
|
|
51561994c5 | ||
|
|
ce0e14bf00 | ||
|
|
dd75ec4aa7 | ||
|
|
65cd42736a | ||
|
|
4792e97177 | ||
|
|
3e9f09e339 | ||
|
|
dba465a094 | ||
|
|
25d1ee7b43 | ||
|
|
2556885622 | ||
|
|
8c088a7026 | ||
|
|
39db8b86fd | ||
|
|
c5830c5d00 | ||
|
|
b68c450710 | ||
|
|
6e2e5761d8 | ||
|
|
6842526d2c | ||
|
|
4ac85d7dc9 | ||
|
|
8170a07886 | ||
|
|
0f1f823736 | ||
|
|
e6c777fb8b | ||
|
|
0bded9698a | ||
|
|
c7f0ea59f0 | ||
|
|
6d62cfb9ae | ||
|
|
803934d84b | ||
|
|
24c97ef92f | ||
|
|
270edf0a76 | ||
|
|
0c5b08e1d2 | ||
|
|
35aef4c837 | ||
|
|
95e7bb01eb | ||
|
|
247e55788a | ||
|
|
bcab66ab8b | ||
|
|
2c87a21da1 | ||
|
|
74b9178017 | ||
|
|
2eb1633b77 | ||
|
|
3fd274ab36 | ||
|
|
f3ab902811 | ||
|
|
b3b1a867c4 | ||
|
|
32561ecb18 |
286
.github/workflows/CICD.yml
vendored
Normal file
286
.github/workflows/CICD.yml
vendored
Normal file
@@ -0,0 +1,286 @@
|
||||
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:-<empty>/false}
|
||||
echo ::set-output name=JOB_DO_FORMAT_TESTING::${JOB_DO_FORMAT_TESTING}
|
||||
- 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 }} -- -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: arm-unknown-linux-gnueabihf , use-cross: use-cross }
|
||||
- { os: ubuntu-18.04 , target: i686-unknown-linux-gnu , use-cross: use-cross }
|
||||
- { os: ubuntu-18.04 , target: i686-unknown-linux-musl , use-cross: use-cross }
|
||||
- { os: ubuntu-18.04 , target: x86_64-unknown-linux-gnu , use-cross: use-cross }
|
||||
- { os: ubuntu-18.04 , target: x86_64-unknown-linux-musl , use-cross: use-cross }
|
||||
- { os: ubuntu-16.04 , target: x86_64-unknown-linux-gnu , 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 ;;
|
||||
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: <https://github.com/rust-lang/rust/issues/47048>, <https://github.com/rust-lang/rust/issues/53454>, <https://github.com/rust-lang/cargo/issues/6754>)
|
||||
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 ;; 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:-<empty>/false}
|
||||
echo ::set-output name=DEPLOY::${DEPLOY}
|
||||
# target-specific options
|
||||
# * CARGO_USE_CROSS (truthy)
|
||||
CARGO_USE_CROSS='true' ; case '${{ matrix.job.use-cross }}' in ''|0|f|false|n|no) unset CARGO_USE_CROSS ;; esac;
|
||||
echo set-output name=CARGO_USE_CROSS::${CARGO_USE_CROSS:-<empty>/false}
|
||||
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-*) unset JOB_DO_TESTING ;; esac;
|
||||
echo set-output name=JOB_DO_TESTING::${JOB_DO_TESTING:-<empty>/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-*) 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="" ;; 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 }}
|
||||
- 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 }}
|
||||
- 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: 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 }}
|
||||
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 <https://github.community/t5/GitHub-Actions/jobs-lt-job-id-gt-if-does-not-work-with-env-secrets/m-p/38549>)
|
||||
# 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 <https://github.community/t5/GitHub-Actions/jobs-lt-job-id-gt-if-does-not-work-with-env-secrets/m-p/38549>)
|
||||
# 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 }}"
|
||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -2,10 +2,8 @@
|
||||
# will have compiled files and executables
|
||||
/target/
|
||||
|
||||
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
|
||||
# More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock
|
||||
Cargo.lock
|
||||
|
||||
# These are backup files generated by rustfmt
|
||||
**/*.rs.bk
|
||||
*.swp
|
||||
.vscode/*
|
||||
*.idea/*
|
||||
76
.travis.yml
76
.travis.yml
@@ -1,76 +0,0 @@
|
||||
# Based on the "trust" template v0.1.2
|
||||
# https://github.com/japaric/trust/tree/v0.1.2
|
||||
|
||||
dist: trusty
|
||||
language: rust
|
||||
services: docker
|
||||
sudo: required
|
||||
|
||||
# TODO Rust builds on stable by default, this can be
|
||||
# overridden on a case by case basis down below.
|
||||
|
||||
env:
|
||||
global:
|
||||
# TODO Update this to match the name of your project.
|
||||
- CRATE_NAME=dust
|
||||
|
||||
matrix:
|
||||
# TODO These are all the build jobs. Adjust as necessary. Comment out what you
|
||||
# don't need
|
||||
include:
|
||||
# Linux
|
||||
- env: TARGET=x86_64-unknown-linux-gnu
|
||||
|
||||
# OSX
|
||||
- env: TARGET=x86_64-apple-darwin
|
||||
os: osx
|
||||
|
||||
before_install:
|
||||
- set -e
|
||||
- rustup self update
|
||||
|
||||
install:
|
||||
- sh ci/install.sh
|
||||
- source ~/.cargo/env || true
|
||||
|
||||
script:
|
||||
- bash ci/script.sh
|
||||
|
||||
after_script: set +e
|
||||
|
||||
before_deploy:
|
||||
- sh ci/before_deploy.sh
|
||||
|
||||
deploy:
|
||||
# TODO update `api_key.secure`
|
||||
# - Create a `public_repo` GitHub token. Go to: https://github.com/settings/tokens/new
|
||||
# - Encrypt it: `travis encrypt 0123456789012345678901234567890123456789
|
||||
# - Paste the output down here
|
||||
api_key:
|
||||
secure: UlU73Td7Bkb2N88ws4YGLWR+4U0IMgiou9QQtMnmpouJFjeUNxtLSPMPODVXP7zq4sKt5HR5B3fX9MW4mKm351fvnQEoihETn06pKiXGnY//SlTPTt67MX9ZOYmd9ohJReMDOZDgqhnGLxfymycGtsLAmdjDZnAl+IMqgg0FMyVFj9Cl9aKxnn12lxQyX4zabHKk8TUKD3By8ZoEUnJMHt3gEtOmbDgS4brcTPeHCzqnYFw73LEnkqvz+JP0XwauJY7Cf8lminKm/klmjCkQji8T9SHI52v1g0Fxpx0ucp2o3vulQrLHXaHvZ6Fr7J0cSXXzaFF3rrGLt4t4jU/+9TZm1+n5k5XuPW4x4NTCC9NmIj/z0/z41t82E9qZhzhtm2Jdsg6H2tNk+C774TYqcmR6GCvfRadfjRp3cA5dh0UwDVjH2MJFxlHDVkl6la0mVVRsCGF3oBKZVk0BDl1womfnmI46o/uU+gLknHN6Ed6PHHPPYDViWd3VKdmHKT7XrkMMUF6HjZUtla689DWIOWZSiV++1dVPcl/1TV+6tTmN4bBtPcLuX7SHRuLp2PI2kATvRMECsa7gZRypW4jKpVn7b2yetX9TVI3i1zR5zkQJ3dPg8sATvYPL53aKH/WsqUg4rzoAlbk9so+++R4bQY69LhV3B511B7EAynoZFdM
|
||||
file_glob: true
|
||||
file: $CRATE_NAME-$TRAVIS_TAG-$TARGET.*
|
||||
on:
|
||||
# TODO Here you can pick which targets will generate binary releases
|
||||
# In this example, there are some targets that are tested using the stable
|
||||
# and nightly channels. This condition makes sure there is only one release
|
||||
# for such targets and that's generated using the stable channel
|
||||
condition: $TRAVIS_RUST_VERSION = stable
|
||||
tags: true
|
||||
provider: releases
|
||||
skip_cleanup: true
|
||||
|
||||
cache: cargo
|
||||
before_cache:
|
||||
# Travis can't cache files that are not readable by "others"
|
||||
- chmod -R a+r $HOME/.cargo
|
||||
|
||||
branches:
|
||||
only:
|
||||
# release tags
|
||||
- /^v\d+\.\d+\.\d+.*$/
|
||||
- master
|
||||
|
||||
notifications:
|
||||
email:
|
||||
on_success: never
|
||||
607
Cargo.lock
generated
Normal file
607
Cargo.lock
generated
Normal file
@@ -0,0 +1,607 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
[[package]]
|
||||
name = "ansi_term"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ansi_term"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "assert_cli"
|
||||
version = "0.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"colored 1.9.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"difference 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"environment 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.47 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atty"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"hermit-abi 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "backtrace"
|
||||
version = "0.3.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"backtrace-sys 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "backtrace-sys"
|
||||
version = "0.1.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "c2-chacha"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.50"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "2.33.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "colored"
|
||||
version = "1.9.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"crossbeam-channel 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"crossbeam-deque 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"crossbeam-epoch 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"crossbeam-queue 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-channel"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-deque"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"crossbeam-epoch 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-epoch"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"memoffset 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-queue"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "difference"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "du-dust"
|
||||
version = "0.5.1"
|
||||
dependencies = [
|
||||
"ansi_term 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"assert_cli 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"jwalk 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lscolors 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num_cpus 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"terminal_size 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "environment"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "failure"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"backtrace 0.3.44 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "failure_derive"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"synstructure 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.1.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "0.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "jwalk"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"crossbeam 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rayon 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.66"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "lscolors"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"ansi_term 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memoffset"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_cpus"
|
||||
version = "1.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"hermit-abi 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"c2-chacha 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_hc"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"crossbeam-deque 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rayon-core 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon-core"
|
||||
version = "1.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"crossbeam-deque 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"crossbeam-queue 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num_cpus 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.1.56"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "remove_dir_all"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-demangle"
|
||||
version = "0.1.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "semver-parser"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.104"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.47"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"itoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ryu 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "synstructure"
|
||||
version = "0.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "terminal_size"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "textwrap"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "vec_map"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.9.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[metadata]
|
||||
"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
|
||||
"checksum ansi_term 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
|
||||
"checksum assert_cli 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a29ab7c0ed62970beb0534d637a8688842506d0ff9157de83286dacd065c8149"
|
||||
"checksum atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||
"checksum autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2"
|
||||
"checksum backtrace 0.3.44 (registry+https://github.com/rust-lang/crates.io-index)" = "e4036b9bf40f3cf16aba72a3d65e8a520fc4bafcdc7079aea8f848c58c5b5536"
|
||||
"checksum backtrace-sys 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)" = "5d6575f128516de27e3ce99689419835fce9643a9b215a14d2b5b685be018491"
|
||||
"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
||||
"checksum c2-chacha 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "214238caa1bf3a496ec3392968969cab8549f96ff30652c9e56885329315f6bb"
|
||||
"checksum cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)" = "95e28fa049fda1c330bcf9d723be7663a899c4679724b34c81e9f5a326aab8cd"
|
||||
"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
||||
"checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9"
|
||||
"checksum colored 1.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8815e2ab78f3a59928fc32e141fbeece88320a240e43f47b2fd64ea3a88a5b3d"
|
||||
"checksum crossbeam 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "69323bff1fb41c635347b8ead484a5ca6c3f11914d784170b158d8449ab07f8e"
|
||||
"checksum crossbeam-channel 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "acec9a3b0b3559f15aee4f90746c4e5e293b701c0f7d3925d24e01645267b68c"
|
||||
"checksum crossbeam-deque 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c3aa945d63861bfe624b55d153a39684da1e8c0bc8fba932f7ee3a3c16cea3ca"
|
||||
"checksum crossbeam-epoch 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5064ebdbf05ce3cb95e45c8b086f72263f4166b29b97f6baff7ef7fe047b55ac"
|
||||
"checksum crossbeam-queue 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c695eeca1e7173472a32221542ae469b3e9aac3a4fc81f7696bcad82029493db"
|
||||
"checksum crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ce446db02cdc3165b94ae73111e570793400d0794e46125cc4056c81cbb039f4"
|
||||
"checksum difference 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198"
|
||||
"checksum either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3"
|
||||
"checksum environment 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1f4b14e20978669064c33b4c1e0fb4083412e40fe56cbea2eae80fd7591503ee"
|
||||
"checksum failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "f8273f13c977665c5db7eb2b99ae520952fe5ac831ae4cd09d80c4c7042b5ed9"
|
||||
"checksum failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0bc225b78e0391e4b8683440bf2e63c2deeeb2ce5189eab46e2b68c6d3725d08"
|
||||
"checksum getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb"
|
||||
"checksum hermit-abi 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "eff2656d88f158ce120947499e971d743c05dbcbed62e5bd2f38f1698bbc3772"
|
||||
"checksum itoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e"
|
||||
"checksum jwalk 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2b3dbf0a8f61baee43a2918ff50ac6a2d3b2c105bc08ed53bc298779f1263409"
|
||||
"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
"checksum libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)" = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558"
|
||||
"checksum lscolors 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ea3b3414b2d015c4fd689815f2551797f3c2296bb241dd709c7da233ec7cba4b"
|
||||
"checksum memoffset 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "75189eb85871ea5c2e2c15abbdd541185f63b408415e5051f5cac122d8c774b9"
|
||||
"checksum num_cpus 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "46203554f085ff89c235cd12f7075f3233af9b11ed7c9e16dfe2560d03313ce6"
|
||||
"checksum ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b"
|
||||
"checksum proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3acb317c6ff86a4e579dfa00fc5e6cca91ecbb4e7eb2df0468805b674eb88548"
|
||||
"checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe"
|
||||
"checksum rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
|
||||
"checksum rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853"
|
||||
"checksum rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
|
||||
"checksum rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
|
||||
"checksum rayon 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "db6ce3297f9c85e16621bb8cca38a06779ffc31bb8184e1be4bed2be4678a098"
|
||||
"checksum rayon-core 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "08a89b46efaf957e52b18062fb2f4660f8b8a4dde1807ca002690868ef2c85a9"
|
||||
"checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84"
|
||||
"checksum remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e"
|
||||
"checksum rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783"
|
||||
"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
|
||||
"checksum ryu 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bfa8506c1de11c9c4e4c38863ccbe02a305c8188e85a05a784c9e11e1c3910c8"
|
||||
"checksum scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b42e15e59b18a828bbf5c58ea01debb36b9b096346de35d941dcb89009f24a0d"
|
||||
"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
|
||||
"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
||||
"checksum serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)" = "414115f25f818d7dfccec8ee535d76949ae78584fc4f79a6f45a904bf8ab4449"
|
||||
"checksum serde_json 1.0.47 (registry+https://github.com/rust-lang/crates.io-index)" = "15913895b61e0be854afd32fd4163fcd2a3df34142cf2cb961b310ce694cbf90"
|
||||
"checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
|
||||
"checksum syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)" = "af6f3550d8dff9ef7dc34d384ac6f107e5d31c8f57d9f28e0081503f547ac8f5"
|
||||
"checksum synstructure 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "67656ea1dc1b41b1451851562ea232ec2e5a80242139f7e679ceccfb5d61f545"
|
||||
"checksum tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9"
|
||||
"checksum terminal_size 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "e25a60e3024df9029a414be05f46318a77c22538861a22170077d0388c0e926e"
|
||||
"checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
|
||||
"checksum unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479"
|
||||
"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
|
||||
"checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a"
|
||||
"checksum wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)" = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
|
||||
"checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6"
|
||||
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
"checksum winapi-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4ccfbf554c6ad11084fb7517daca16cfdcaccbdadba4fc336f032a8b12c2ad80"
|
||||
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
43
Cargo.toml
43
Cargo.toml
@@ -1,11 +1,42 @@
|
||||
[package]
|
||||
name = "dust"
|
||||
version = "0.2.3"
|
||||
name = "du-dust"
|
||||
description = "A more intuitive version of du"
|
||||
version = "0.5.1"
|
||||
authors = ["bootandy <bootandy@gmail.com>", "nebkor <code@ardent.nebcorp.com>"]
|
||||
edition = "2018"
|
||||
|
||||
documentation = "https://github.com/bootandy/dust"
|
||||
homepage = "https://github.com/bootandy/dust"
|
||||
repository = "https://github.com/bootandy/dust"
|
||||
|
||||
keywords = ["du", "command-line", "disk", "disk-usage"]
|
||||
categories = ["command-line-utilities"]
|
||||
license = "Apache-2.0"
|
||||
|
||||
[badges]
|
||||
travis-ci = {repository = "https://travis-ci.org/bootandy/dust"}
|
||||
|
||||
[[bin]]
|
||||
name = "dust"
|
||||
path = "src/main.rs"
|
||||
|
||||
[dependencies]
|
||||
ansi_term = "0.11"
|
||||
clap = "2.31"
|
||||
assert_cli = "0.5"
|
||||
tempfile = "3"
|
||||
ansi_term = "=0.12"
|
||||
clap = "=2.33"
|
||||
jwalk = "0.4.0"
|
||||
lscolors = "0.6.0"
|
||||
num_cpus = "1.12"
|
||||
terminal_size = "0.1.10"
|
||||
unicode-width = "0.1.7"
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
winapi-util = "0.1"
|
||||
|
||||
[dev-dependencies]
|
||||
assert_cli = "=0.6"
|
||||
tempfile = "=3"
|
||||
|
||||
|
||||
[[test]]
|
||||
name = "integration"
|
||||
path = "tests/tests.rs"
|
||||
|
||||
67
README.md
67
README.md
@@ -1,51 +1,56 @@
|
||||
|
||||
[](https://travis-ci.org/bootandy/dust)
|
||||
|
||||
|
||||
# Dust
|
||||
du + rust = dust. A rust alternative to du
|
||||
|
||||
[Releases](https://github.com/bootandy/dust/releases)
|
||||
To install:
|
||||
* Download linux / mac binary from [Releases](https://github.com/bootandy/dust/releases)
|
||||
* unzip file: tar -xvf <file>
|
||||
* copy file to search path: sudo mv dust /usr/local/bin/
|
||||
du + rust = dust. Like du but more intuitive.
|
||||
|
||||
Unlike du, dust is meant to give you an instant overview of which directories are using disk space without requiring sort or head. Dust does not count file system blocks; it uses file sizes instead. Dust will print a maximum of 1 'Did not have permissions message'.
|
||||
# Why
|
||||
|
||||
Because I want an easy way to see where my disk is being used.
|
||||
|
||||
Dust will list the 15 biggest sub directories and will smartly recurse down the tree to find the larger ones. There is no need for a '-d' flag or a '-h' flag. The largest sub directory will have its size shown in red
|
||||
# Demo
|
||||

|
||||
|
||||
## Install
|
||||
|
||||
#### Cargo
|
||||
|
||||
* `cargo install du-dust`
|
||||
|
||||
#### 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.
|
||||
|
||||
## Usage
|
||||
|
||||
```
|
||||
Usage: dust
|
||||
Usage: dust <dir>
|
||||
Usage: dust <dir> <another_dir> <and_more>
|
||||
Usage: dust -p <dir> (full-path - does not shorten the path of the subdirectories)
|
||||
Usage: dust -s <dir> (apparent-size - shows the length of the file as opposed to the amount of disk space it uses)
|
||||
Usage: dust -n 30 <dir> (Shows 30 directories not 15)
|
||||
Usage: dust -n 30 <dir> (shows 30 directories instead of the default)
|
||||
Usage: dust -d 3 <dir> (shows 3 levels of subdirectories)
|
||||
Usage: dust -r <dir> (reverse order of output, with root at the lowest)
|
||||
Usage: dust -x <dir> (only show directories on the same filesystem)
|
||||
Usage: dust -X ignore <dir> (ignore all files and directories with the name 'ignore')
|
||||
Usage: dust -b <dir> (do not show percentages or draw ASCII bars)
|
||||
```
|
||||
|
||||
|
||||
```
|
||||
djin:git/dust> dust
|
||||
65M .
|
||||
65M └─┬ ./target
|
||||
49M ├─┬ ./target/debug
|
||||
26M │ ├─┬ ./target/debug/deps
|
||||
21M │ │ └── ./target/debug/deps/libclap-9e6625ac8ff074ad.rlib
|
||||
13M │ ├── ./target/debug/dust
|
||||
8.9M │ └─┬ ./target/debug/incremental
|
||||
6.7M │ ├─┬ ./target/debug/incremental/dust-2748eiei2tcnp
|
||||
6.7M │ │ └─┬ ./target/debug/incremental/dust-2748eiei2tcnp/s-ezd6jnik5u-163pyem-1aab9ncf5glum
|
||||
3.0M │ │ └── ./target/debug/incremental/dust-2748eiei2tcnp/s-ezd6jnik5u-163pyem-1aab9ncf5glum/dep-graph.bin
|
||||
2.2M │ └─┬ ./target/debug/incremental/dust-1dlon65p8m3vl
|
||||
2.2M │ └── ./target/debug/incremental/dust-1dlon65p8m3vl/s-ezd6jncecv-1xsnfd0-4dw9l1r2th2t
|
||||
15M └─┬ ./target/release
|
||||
9.2M ├─┬ ./target/release/deps
|
||||
6.7M │ └── ./target/release/deps/libclap-87bc2534ea57f044.rlib
|
||||
5.9M └── ./target/release/dust
|
||||
```
|
||||
Performance: dust is currently about 4 times slower than du.
|
||||
## Alternatives
|
||||
|
||||
Alternatives:
|
||||
* [NCDU](https://dev.yorhel.nl/ncdu)
|
||||
* [dutree](https://github.com/nachoparker/dutree)
|
||||
* 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.
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#!/usr/bin/env bash
|
||||
# This script takes care of building your crate and packaging it for release
|
||||
|
||||
set -ex
|
||||
|
||||
6
ci/how2publish.txt
Normal file
6
ci/how2publish.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
# ----------- To do a release ---------
|
||||
# tag a commit and push:
|
||||
# git tag v0.4.5
|
||||
# git push origin v0.4.5
|
||||
|
||||
# cargo publish to put it in crates.io
|
||||
@@ -1,3 +1,4 @@
|
||||
#!/usr/bin/env bash
|
||||
set -ex
|
||||
|
||||
main() {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#!/usr/bin/env bash
|
||||
# This script takes care of testing your crate
|
||||
|
||||
set -ex
|
||||
|
||||
BIN
media/snap.png
Normal file
BIN
media/snap.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 61 KiB |
367
src/display.rs
367
src/display.rs
@@ -1,100 +1,321 @@
|
||||
extern crate ansi_term;
|
||||
|
||||
use crate::utils::Node;
|
||||
|
||||
use self::ansi_term::Colour::Fixed;
|
||||
use dust::Node;
|
||||
use lscolors::{LsColors, Style};
|
||||
|
||||
use terminal_size::{terminal_size, Height, Width};
|
||||
|
||||
use unicode_width::UnicodeWidthStr;
|
||||
|
||||
use std::cmp::max;
|
||||
use std::cmp::min;
|
||||
use std::fs;
|
||||
use std::iter::repeat;
|
||||
use std::path::Path;
|
||||
|
||||
static UNITS: [char; 4] = ['T', 'G', 'M', 'K'];
|
||||
static BLOCKS: [char; 5] = ['█', '▓', '▒', '░', ' '];
|
||||
static DEFAULT_TERMINAL_WIDTH: u16 = 80;
|
||||
|
||||
pub fn draw_it(permissions: bool, heads: &Vec<Node>, to_display: &Vec<&Node>) -> () {
|
||||
if !permissions {
|
||||
eprintln!("Did not have permissions for all directories");
|
||||
pub struct DisplayData {
|
||||
pub short_paths: bool,
|
||||
pub is_reversed: bool,
|
||||
pub colors_on: bool,
|
||||
pub base_size: u64,
|
||||
pub longest_string_length: usize,
|
||||
pub ls_colors: LsColors,
|
||||
}
|
||||
|
||||
for d in to_display {
|
||||
if heads.contains(d) {
|
||||
display_node(d, &to_display, true, "")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn display_node<S: Into<String>>(
|
||||
node_to_print: &Node,
|
||||
to_display: &Vec<&Node>,
|
||||
is_first: bool,
|
||||
indentation_str: S,
|
||||
) {
|
||||
let mut is = indentation_str.into();
|
||||
print_this_node(node_to_print, is_first, is.as_ref());
|
||||
|
||||
is = is.replace("└─┬", " ");
|
||||
is = is.replace("└──", " ");
|
||||
is = is.replace("├──", "│ ");
|
||||
is = is.replace("├─┬", "│ ");
|
||||
|
||||
let printable_node_slashes = node_to_print.name().matches('/').count();
|
||||
|
||||
let mut num_siblings = to_display.iter().fold(0, |a, b| {
|
||||
if node_to_print.children().contains(b)
|
||||
&& b.name().matches('/').count() == printable_node_slashes + 1
|
||||
{
|
||||
a + 1
|
||||
impl DisplayData {
|
||||
#[allow(clippy::collapsible_if)]
|
||||
fn get_tree_chars(&self, was_i_last: bool, has_children: bool) -> &'static str {
|
||||
if self.is_reversed {
|
||||
if was_i_last {
|
||||
if has_children {
|
||||
"┌─┴"
|
||||
} else {
|
||||
a
|
||||
"┌──"
|
||||
}
|
||||
});
|
||||
|
||||
let mut is_biggest = true;
|
||||
for node in to_display {
|
||||
if node_to_print.children().contains(node) {
|
||||
let has_display_children = node.children()
|
||||
.iter()
|
||||
.fold(false, |has_kids, n| has_kids || to_display.contains(&n));
|
||||
|
||||
let has_children = node.children().len() > 0 && has_display_children;
|
||||
if node.name().matches('/').count() == printable_node_slashes + 1 {
|
||||
num_siblings -= 1;
|
||||
|
||||
let tree_chars = {
|
||||
if num_siblings == 0 {
|
||||
} else if has_children {
|
||||
"├─┴"
|
||||
} else {
|
||||
"├──"
|
||||
}
|
||||
} else {
|
||||
if was_i_last {
|
||||
if has_children {
|
||||
"└─┬"
|
||||
} else {
|
||||
"└──"
|
||||
}
|
||||
} else {
|
||||
if has_children {
|
||||
} else if has_children {
|
||||
"├─┬"
|
||||
} else {
|
||||
"├──"
|
||||
}
|
||||
}
|
||||
};
|
||||
display_node(&node, to_display, is_biggest, is.to_string() + tree_chars);
|
||||
is_biggest = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn print_this_node(node: &Node, is_biggest: bool, indentation: &str) {
|
||||
let pretty_size = format!("{:>5}", human_readable_number(node.size()),);
|
||||
println!(
|
||||
"{}",
|
||||
format_string(node.name(), is_biggest, pretty_size.as_ref(), indentation)
|
||||
)
|
||||
}
|
||||
|
||||
pub fn format_string(dir_name: &str, is_biggest: bool, size: &str, indentation: &str) -> String {
|
||||
format!(
|
||||
"{} {} {}",
|
||||
if is_biggest {
|
||||
Fixed(196).paint(size)
|
||||
fn is_biggest(&self, num_siblings: usize, max_siblings: u64) -> bool {
|
||||
if self.is_reversed {
|
||||
num_siblings == (max_siblings - 1) as usize
|
||||
} else {
|
||||
Fixed(7).paint(size)
|
||||
num_siblings == 0
|
||||
}
|
||||
}
|
||||
|
||||
fn is_last(&self, num_siblings: usize, max_siblings: u64) -> bool {
|
||||
if self.is_reversed {
|
||||
num_siblings == 0
|
||||
} else {
|
||||
num_siblings == (max_siblings - 1) as usize
|
||||
}
|
||||
}
|
||||
|
||||
fn percent_size(&self, node: &Node) -> f32 {
|
||||
let result = node.size as f32 / self.base_size as f32;
|
||||
if result.is_normal() {
|
||||
result
|
||||
} else {
|
||||
0.0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_children_from_node(node: Node, is_reversed: bool) -> impl Iterator<Item = Node> {
|
||||
if is_reversed {
|
||||
let n: Vec<Node> = node.children.into_iter().rev().map(|a| a).collect();
|
||||
n.into_iter()
|
||||
} else {
|
||||
node.children.into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
struct DrawData<'a> {
|
||||
indent: String,
|
||||
percent_bar: String,
|
||||
display_data: &'a DisplayData,
|
||||
}
|
||||
|
||||
impl DrawData<'_> {
|
||||
fn get_new_indent(&self, has_children: bool, was_i_last: bool) -> String {
|
||||
let chars = self.display_data.get_tree_chars(was_i_last, has_children);
|
||||
self.indent.to_string() + chars
|
||||
}
|
||||
|
||||
fn generate_bar(&self, node: &Node, level: usize) -> String {
|
||||
let chars_in_bar = self.percent_bar.chars().count();
|
||||
let num_bars = chars_in_bar as f32 * self.display_data.percent_size(node);
|
||||
let mut num_not_my_bar = (chars_in_bar as i32) - num_bars as i32;
|
||||
|
||||
let mut new_bar = "".to_string();
|
||||
let idx = 5 - min(5, max(1, level));
|
||||
|
||||
for c in self.percent_bar.chars() {
|
||||
num_not_my_bar -= 1;
|
||||
if num_not_my_bar <= 0 {
|
||||
new_bar.push(BLOCKS[0]);
|
||||
} else if c == BLOCKS[0] {
|
||||
new_bar.push(BLOCKS[idx]);
|
||||
} else {
|
||||
new_bar.push(c);
|
||||
}
|
||||
}
|
||||
new_bar
|
||||
}
|
||||
}
|
||||
|
||||
fn get_width_of_terminal() -> u16 {
|
||||
// Windows CI runners detect a very low terminal width
|
||||
if let Some((Width(w), Height(_h))) = terminal_size() {
|
||||
max(w, DEFAULT_TERMINAL_WIDTH)
|
||||
} else {
|
||||
DEFAULT_TERMINAL_WIDTH
|
||||
}
|
||||
}
|
||||
|
||||
pub fn draw_it(
|
||||
permissions: bool,
|
||||
use_full_path: bool,
|
||||
is_reversed: bool,
|
||||
no_colors: bool,
|
||||
no_percents: bool,
|
||||
root_node: Node,
|
||||
) {
|
||||
if !permissions {
|
||||
eprintln!("Did not have permissions for all directories");
|
||||
}
|
||||
let longest_string_length = root_node
|
||||
.children
|
||||
.iter()
|
||||
.map(|c| find_longest_dir_name(&c, " ", !use_full_path))
|
||||
.fold(0, max);
|
||||
let terminal_width = get_width_of_terminal() - 16;
|
||||
|
||||
let max_bar_length = if no_percents || longest_string_length >= terminal_width as usize {
|
||||
0
|
||||
} else {
|
||||
terminal_width as usize - longest_string_length
|
||||
};
|
||||
|
||||
// handle usize error also add do not show fancy output option
|
||||
let bar_text = repeat(BLOCKS[0]).take(max_bar_length).collect::<String>();
|
||||
|
||||
for c in get_children_from_node(root_node, is_reversed) {
|
||||
let display_data = DisplayData {
|
||||
short_paths: !use_full_path,
|
||||
is_reversed,
|
||||
colors_on: !no_colors,
|
||||
base_size: c.size,
|
||||
longest_string_length,
|
||||
ls_colors: LsColors::from_env().unwrap_or_default(),
|
||||
};
|
||||
let draw_data = DrawData {
|
||||
indent: "".to_string(),
|
||||
percent_bar: bar_text.clone(),
|
||||
display_data: &display_data,
|
||||
};
|
||||
display_node(c, &draw_data, true, true);
|
||||
}
|
||||
}
|
||||
|
||||
// We can probably pass depth instead of indent here.
|
||||
// It is ugly to feed in ' ' instead of the actual tree characters but we don't need them yet.
|
||||
fn find_longest_dir_name(node: &Node, indent: &str, long_paths: bool) -> usize {
|
||||
let printable_name = get_printable_name(&node.name, long_paths);
|
||||
let longest = get_unicode_width_of_indent_and_name(indent, &printable_name);
|
||||
|
||||
// each none root tree drawing is 2 chars
|
||||
let full_indent: String = indent.to_string() + " ";
|
||||
node.children
|
||||
.iter()
|
||||
.map(|c| find_longest_dir_name(c, &*full_indent, long_paths))
|
||||
.fold(longest, max)
|
||||
}
|
||||
|
||||
fn display_node(node: Node, draw_data: &DrawData, is_biggest: bool, is_last: bool) {
|
||||
let indent2 = draw_data.get_new_indent(!node.children.is_empty(), is_last);
|
||||
// hacky way of working out how deep we are in the tree
|
||||
let level = ((indent2.chars().count() - 1) / 2) - 1;
|
||||
let bar_text = draw_data.generate_bar(&node, level);
|
||||
|
||||
let to_print = format_string(
|
||||
&node,
|
||||
&*indent2,
|
||||
&*bar_text,
|
||||
is_biggest,
|
||||
draw_data.display_data,
|
||||
);
|
||||
|
||||
if !draw_data.display_data.is_reversed {
|
||||
println!("{}", to_print)
|
||||
}
|
||||
|
||||
let dd = DrawData {
|
||||
indent: clean_indentation_string(&*indent2),
|
||||
percent_bar: bar_text,
|
||||
display_data: draw_data.display_data,
|
||||
};
|
||||
let num_siblings = node.children.len() as u64;
|
||||
|
||||
for (count, c) in get_children_from_node(node, draw_data.display_data.is_reversed).enumerate() {
|
||||
let is_biggest = dd.display_data.is_biggest(count, num_siblings);
|
||||
let was_i_last = dd.display_data.is_last(count, num_siblings);
|
||||
display_node(c, &dd, is_biggest, was_i_last);
|
||||
}
|
||||
|
||||
if draw_data.display_data.is_reversed {
|
||||
println!("{}", to_print)
|
||||
}
|
||||
}
|
||||
|
||||
fn clean_indentation_string(s: &str) -> String {
|
||||
let mut is: String = s.into();
|
||||
// For reversed:
|
||||
is = is.replace("┌─┴", " ");
|
||||
is = is.replace("┌──", " ");
|
||||
is = is.replace("├─┴", "│ ");
|
||||
is = is.replace("─┴", " ");
|
||||
// For normal
|
||||
is = is.replace("└─┬", " ");
|
||||
is = is.replace("└──", " ");
|
||||
is = is.replace("├─┬", "│ ");
|
||||
is = is.replace("─┬", " ");
|
||||
// For both
|
||||
is = is.replace("├──", "│ ");
|
||||
is
|
||||
}
|
||||
|
||||
fn get_printable_name<P: AsRef<Path>>(dir_name: &P, long_paths: bool) -> String {
|
||||
let dir_name = dir_name.as_ref();
|
||||
let printable_name = {
|
||||
if long_paths {
|
||||
match dir_name.parent() {
|
||||
Some(prefix) => match dir_name.strip_prefix(prefix) {
|
||||
Ok(base) => base,
|
||||
Err(_) => dir_name,
|
||||
},
|
||||
indentation,
|
||||
dir_name,
|
||||
)
|
||||
None => dir_name,
|
||||
}
|
||||
} else {
|
||||
dir_name
|
||||
}
|
||||
};
|
||||
printable_name.display().to_string()
|
||||
}
|
||||
|
||||
fn get_unicode_width_of_indent_and_name(indent: &str, name: &str) -> usize {
|
||||
let indent_and_name = format!("{} {}", indent, name);
|
||||
UnicodeWidthStr::width(&*indent_and_name)
|
||||
}
|
||||
|
||||
pub fn format_string(
|
||||
node: &Node,
|
||||
indent: &str,
|
||||
percent_bar: &str,
|
||||
is_biggest: bool,
|
||||
display_data: &DisplayData,
|
||||
) -> String {
|
||||
let pretty_size = format!("{:>5}", human_readable_number(node.size));
|
||||
|
||||
let percent_size_str = format!("{:.0}%", display_data.percent_size(node) * 100.0);
|
||||
|
||||
let name = get_printable_name(&node.name, display_data.short_paths);
|
||||
let width = get_unicode_width_of_indent_and_name(indent, &name);
|
||||
|
||||
let name_and_padding = name
|
||||
+ &(repeat(" ")
|
||||
.take(display_data.longest_string_length - width)
|
||||
.collect::<String>());
|
||||
|
||||
let percents = if percent_bar != "" {
|
||||
format!("│{} │ {:>4}", percent_bar, percent_size_str)
|
||||
} else {
|
||||
"".into()
|
||||
};
|
||||
|
||||
let pretty_size = if is_biggest && display_data.colors_on {
|
||||
format!("{}", Fixed(196).paint(pretty_size))
|
||||
} else {
|
||||
pretty_size
|
||||
};
|
||||
|
||||
let pretty_name = if display_data.colors_on {
|
||||
let meta_result = fs::metadata(node.name.clone());
|
||||
let directory_color = display_data
|
||||
.ls_colors
|
||||
.style_for_path_with_metadata(node.name.clone(), meta_result.as_ref().ok());
|
||||
let ansi_style = directory_color
|
||||
.map(Style::to_ansi_term_style)
|
||||
.unwrap_or_default();
|
||||
format!("{}", ansi_style.paint(name_and_padding))
|
||||
} else {
|
||||
name_and_padding
|
||||
};
|
||||
|
||||
format!("{} {} {}{}", pretty_size, indent, pretty_name, percents)
|
||||
}
|
||||
|
||||
fn human_readable_number(size: u64) -> String {
|
||||
|
||||
68
src/lib.rs
68
src/lib.rs
@@ -1,68 +0,0 @@
|
||||
use std::cmp::{Eq, Ord, Ordering, PartialEq, PartialOrd};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Node {
|
||||
name: String,
|
||||
size: u64,
|
||||
children: Vec<Node>,
|
||||
}
|
||||
|
||||
impl Node {
|
||||
pub fn new<S: Into<String>>(name: S, size: u64, children: Vec<Node>) -> Self {
|
||||
Node {
|
||||
children: children,
|
||||
name: name.into(),
|
||||
size: size,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn children(&self) -> &Vec<Node> {
|
||||
&self.children
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &String {
|
||||
&self.name
|
||||
}
|
||||
|
||||
pub fn size(&self) -> u64 {
|
||||
self.size
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for Node {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
if self.size > other.size {
|
||||
Ordering::Less
|
||||
} else if self.size < other.size {
|
||||
Ordering::Greater
|
||||
} else {
|
||||
let my_slashes = self.name.matches('/').count();
|
||||
let other_slashes = other.name.matches('/').count();
|
||||
|
||||
if my_slashes > other_slashes {
|
||||
Ordering::Greater
|
||||
} else if my_slashes < other_slashes {
|
||||
Ordering::Less
|
||||
} else {
|
||||
if self.name < other.name {
|
||||
Ordering::Less
|
||||
} else if self.name > other.name {
|
||||
Ordering::Greater
|
||||
} else {
|
||||
Ordering::Equal
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
impl PartialOrd for Node {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
impl PartialEq for Node {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
(&self.name, self.size) == (&other.name, other.size)
|
||||
}
|
||||
}
|
||||
impl Eq for Node {}
|
||||
217
src/main.rs
217
src/main.rs
@@ -1,48 +1,235 @@
|
||||
#[macro_use]
|
||||
extern crate clap;
|
||||
extern crate assert_cli;
|
||||
extern crate dust;
|
||||
extern crate unicode_width;
|
||||
|
||||
use self::display::draw_it;
|
||||
use crate::utils::is_a_parent_of;
|
||||
use clap::{App, AppSettings, Arg};
|
||||
use utils::{find_big_ones, get_dir_tree};
|
||||
use std::cmp::max;
|
||||
use std::path::PathBuf;
|
||||
use terminal_size::{terminal_size, Height, Width};
|
||||
use utils::{find_big_ones, get_dir_tree, simplify_dir_names, sort, trim_deep_ones, Node};
|
||||
|
||||
mod display;
|
||||
mod utils;
|
||||
|
||||
static DEFAULT_NUMBER_OF_LINES: &'static str = "15";
|
||||
static DEFAULT_NUMBER_OF_LINES: usize = 30;
|
||||
|
||||
#[cfg(windows)]
|
||||
fn init_color(no_color: bool) -> bool {
|
||||
// Required for windows 10
|
||||
// Fails to resolve for windows 8 so disable color
|
||||
match ansi_term::enable_ansi_support() {
|
||||
Ok(_) => no_color,
|
||||
Err(_) => {
|
||||
eprintln!(
|
||||
"This version of Windows does not support ANSI colors, setting no_color flag"
|
||||
);
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
fn init_color(no_color: bool) -> bool {
|
||||
no_color
|
||||
}
|
||||
|
||||
fn get_height_of_terminal() -> usize {
|
||||
// Windows CI runners detect a terminal height of 0
|
||||
if let Some((Width(_w), Height(h))) = terminal_size() {
|
||||
max(h as usize, DEFAULT_NUMBER_OF_LINES) - 10
|
||||
} else {
|
||||
DEFAULT_NUMBER_OF_LINES - 10
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let default_height = get_height_of_terminal();
|
||||
let def_num_str = default_height.to_string();
|
||||
|
||||
let options = App::new("Dust")
|
||||
.about("Like du but more intuitive")
|
||||
.version(crate_version!())
|
||||
.setting(AppSettings::TrailingVarArg)
|
||||
.arg(
|
||||
Arg::with_name("depth")
|
||||
.short("d")
|
||||
.long("depth")
|
||||
.help("Depth to show")
|
||||
.takes_value(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("threads")
|
||||
.short("t")
|
||||
.long("threads")
|
||||
.help("Number of threads to spawn simultaneously")
|
||||
.takes_value(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("number_of_lines")
|
||||
.short("n")
|
||||
.long("number-of-lines")
|
||||
.help("Number of lines of output to show")
|
||||
.takes_value(true)
|
||||
.default_value(DEFAULT_NUMBER_OF_LINES),
|
||||
.default_value(def_num_str.as_ref()),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("use_apparent_size")
|
||||
Arg::with_name("display_full_paths")
|
||||
.short("p")
|
||||
.long("full-paths")
|
||||
.help("Subdirectories will not have their path shortened"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("ignore_directory")
|
||||
.short("X")
|
||||
.long("ignore-directory")
|
||||
.takes_value(true)
|
||||
.number_of_values(1)
|
||||
.multiple(true)
|
||||
.help("Exclude any file or directory with this name"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("limit_filesystem")
|
||||
.short("x")
|
||||
.long("limit-filesystem")
|
||||
.help("Only count the files and directories on the same filesystem as the supplied directory"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("display_apparent_size")
|
||||
.short("s")
|
||||
.help("If set will use file length. Otherwise we use blocks"),
|
||||
.long("apparent-size")
|
||||
.help("Use file length instead of blocks"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("reverse")
|
||||
.short("r")
|
||||
.long("reverse")
|
||||
.help("Print tree upside down (biggest highest)"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("no_colors")
|
||||
.short("c")
|
||||
.long("no-colors")
|
||||
.help("No colors will be printed (normally largest directories are colored)"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("no_bars")
|
||||
.short("b")
|
||||
.long("no-percent-bars")
|
||||
.help("No percent bars or percentages will be displayed"),
|
||||
)
|
||||
.arg(Arg::with_name("inputs").multiple(true))
|
||||
.get_matches();
|
||||
|
||||
let filenames = {
|
||||
let target_dirs = {
|
||||
match options.values_of("inputs") {
|
||||
None => vec!["."],
|
||||
Some(r) => r.collect(),
|
||||
}
|
||||
};
|
||||
let number_of_lines = value_t!(options.value_of("number_of_lines"), usize).unwrap();
|
||||
let use_apparent_size = options.is_present("use_apparent_size");
|
||||
|
||||
let (permissions, node_per_top_level_dir) = get_dir_tree(&filenames, use_apparent_size);
|
||||
let slice_it = find_big_ones(&node_per_top_level_dir, number_of_lines);
|
||||
draw_it(permissions, &node_per_top_level_dir, &slice_it);
|
||||
let number_of_lines = match value_t!(options.value_of("number_of_lines"), usize) {
|
||||
Ok(v) => v,
|
||||
Err(_) => {
|
||||
eprintln!("Ignoring bad value for number_of_lines");
|
||||
default_height
|
||||
}
|
||||
};
|
||||
|
||||
let temp_threads = options.value_of("threads").and_then(|threads| {
|
||||
threads
|
||||
.parse::<usize>()
|
||||
.map_err(|_| eprintln!("Ignoring bad value for threads: {:?}", threads))
|
||||
.ok()
|
||||
});
|
||||
// Bug in JWalk
|
||||
// https://github.com/jessegrosjean/jwalk/issues/15
|
||||
// We force it to use 2 threads if there is only 1 cpu
|
||||
// as JWalk breaks if it tries to run on a single cpu
|
||||
let threads = {
|
||||
if temp_threads.is_none() && num_cpus::get() == 1 {
|
||||
Some(2)
|
||||
} else {
|
||||
temp_threads
|
||||
}
|
||||
};
|
||||
|
||||
let depth = options.value_of("depth").and_then(|depth| {
|
||||
depth
|
||||
.parse::<u64>()
|
||||
.map(|v| v + 1)
|
||||
.map_err(|_| eprintln!("Ignoring bad value for depth"))
|
||||
.ok()
|
||||
});
|
||||
if options.is_present("depth") && number_of_lines != default_height {
|
||||
eprintln!("Use either -n or -d. Not both");
|
||||
return;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
let no_colors = init_color(options.is_present("no_colors"));
|
||||
let use_apparent_size = options.is_present("display_apparent_size");
|
||||
let limit_filesystem = options.is_present("limit_filesystem");
|
||||
let ignore_directories = match options.values_of("ignore_directory") {
|
||||
Some(i) => Some(i.map(PathBuf::from).collect()),
|
||||
None => None,
|
||||
};
|
||||
|
||||
let simplified_dirs = simplify_dir_names(target_dirs);
|
||||
let (permissions, nodes) = get_dir_tree(
|
||||
&simplified_dirs,
|
||||
&ignore_directories,
|
||||
use_apparent_size,
|
||||
limit_filesystem,
|
||||
threads,
|
||||
);
|
||||
let sorted_data = sort(nodes);
|
||||
let biggest_ones = {
|
||||
match depth {
|
||||
None => find_big_ones(sorted_data, number_of_lines + simplified_dirs.len()),
|
||||
Some(d) => trim_deep_ones(sorted_data, d, &simplified_dirs),
|
||||
}
|
||||
};
|
||||
let tree = build_tree(biggest_ones, depth);
|
||||
|
||||
draw_it(
|
||||
permissions,
|
||||
options.is_present("display_full_paths"),
|
||||
!options.is_present("reverse"),
|
||||
no_colors,
|
||||
options.is_present("no_bars"),
|
||||
tree,
|
||||
);
|
||||
}
|
||||
|
||||
fn build_tree(biggest_ones: Vec<(PathBuf, u64)>, depth: Option<u64>) -> Node {
|
||||
let mut top_parent = Node::default();
|
||||
|
||||
// assume sorted order
|
||||
for b in biggest_ones {
|
||||
let n = Node {
|
||||
name: b.0,
|
||||
size: b.1,
|
||||
children: Vec::default(),
|
||||
};
|
||||
recursively_build_tree(&mut top_parent, n, depth);
|
||||
}
|
||||
top_parent
|
||||
}
|
||||
|
||||
fn recursively_build_tree(parent_node: &mut Node, new_node: Node, depth: Option<u64>) {
|
||||
let new_depth = match depth {
|
||||
None => None,
|
||||
Some(0) => return,
|
||||
Some(d) => Some(d - 1),
|
||||
};
|
||||
if let Some(c) = parent_node
|
||||
.children
|
||||
.iter_mut()
|
||||
.find(|c| is_a_parent_of(&c.name, &new_node.name))
|
||||
{
|
||||
recursively_build_tree(c, new_node, new_depth);
|
||||
} else {
|
||||
parent_node.children.push(new_node);
|
||||
}
|
||||
}
|
||||
|
||||
1
src/test_dir2/dir/hello
Normal file
1
src/test_dir2/dir/hello
Normal file
@@ -0,0 +1 @@
|
||||
hello
|
||||
1
src/test_dir2/dir_name_clash
Normal file
1
src/test_dir2/dir_name_clash
Normal file
@@ -0,0 +1 @@
|
||||
hello
|
||||
1
src/test_dir2/dir_substring/hello
Normal file
1
src/test_dir2/dir_substring/hello
Normal file
@@ -0,0 +1 @@
|
||||
hello
|
||||
0
src/test_dir3/ラウトは難しいです!.japan
Normal file
0
src/test_dir3/ラウトは難しいです!.japan
Normal file
0
src/test_dir3/👩.unicode
Normal file
0
src/test_dir3/👩.unicode
Normal file
140
src/tests.rs
140
src/tests.rs
@@ -1,140 +0,0 @@
|
||||
extern crate ansi_term;
|
||||
extern crate tempfile;
|
||||
use self::tempfile::Builder;
|
||||
use self::tempfile::TempDir;
|
||||
use super::*;
|
||||
use display::format_string;
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
use std::path::PathBuf;
|
||||
use std::process::Command;
|
||||
|
||||
#[test]
|
||||
pub fn test_main() {
|
||||
let r = format!(
|
||||
"{}
|
||||
{}
|
||||
{}
|
||||
{}",
|
||||
format_string("src/test_dir", true, " 4.0K", ""),
|
||||
format_string("src/test_dir/many", true, " 4.0K", "└─┬",),
|
||||
format_string("src/test_dir/many/hello_file", true, " 4.0K", " ├──",),
|
||||
format_string("src/test_dir/many/a_file", false, " 0B", " └──",),
|
||||
);
|
||||
|
||||
assert_cli::Assert::main_binary()
|
||||
.with_args(&["src/test_dir"])
|
||||
.stdout()
|
||||
.is(r)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_apparent_size() {
|
||||
let r = format!(
|
||||
"{}",
|
||||
format_string("src/test_dir/many/hello_file", true, " 6B", " ├──",),
|
||||
);
|
||||
|
||||
assert_cli::Assert::main_binary()
|
||||
.with_args(&["-s", "src/test_dir"])
|
||||
.stdout()
|
||||
.contains(r)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
#[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_name.to_str().unwrap();
|
||||
let c = Command::new("ln")
|
||||
.arg("-s")
|
||||
.arg(file_path_s)
|
||||
.arg(link_name_s)
|
||||
.output();
|
||||
assert!(c.is_ok());
|
||||
|
||||
let r = format!(
|
||||
"{}
|
||||
{}
|
||||
{}",
|
||||
format_string(dir_s, true, " 8.0K", ""),
|
||||
format_string(file_path_s, true, " 4.0K", "├──",),
|
||||
format_string(link_name_s, false, " 4.0K", "└──",),
|
||||
);
|
||||
|
||||
assert_cli::Assert::main_binary()
|
||||
.with_args(&[dir_s])
|
||||
.stdout()
|
||||
.contains(r)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
// Hard links are ignored as the inode is the same as the file
|
||||
#[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");
|
||||
let link_name_s = link_name.to_str().unwrap();
|
||||
let c = Command::new("ln")
|
||||
.arg(file_path_s)
|
||||
.arg(link_name_s)
|
||||
.output();
|
||||
assert!(c.is_ok());
|
||||
|
||||
let r = format!(
|
||||
"{}
|
||||
{}",
|
||||
format_string(dir_s, true, " 4.0K", ""),
|
||||
format_string(file_path_s, true, " 4.0K", "└──")
|
||||
);
|
||||
|
||||
assert_cli::Assert::main_binary()
|
||||
.with_args(&[dir_s])
|
||||
.stdout()
|
||||
.contains(r)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
//Check we don't recurse down an infinite symlink tree
|
||||
#[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_name.to_str().unwrap();
|
||||
|
||||
let c = Command::new("cd").arg(dir_s).output();
|
||||
assert!(c.is_ok());
|
||||
let c = Command::new("ln")
|
||||
.arg("-s")
|
||||
.arg(".")
|
||||
.arg(link_name_s)
|
||||
.output();
|
||||
assert!(c.is_ok());
|
||||
|
||||
let r = format!("{}", format_string(dir_s, true, " 4.0K", ""));
|
||||
|
||||
assert_cli::Assert::main_binary()
|
||||
.with_args(&[dir_s])
|
||||
.stdout()
|
||||
.contains(r)
|
||||
.unwrap();
|
||||
}
|
||||
468
src/utils/mod.rs
468
src/utils/mod.rs
@@ -1,113 +1,403 @@
|
||||
use jwalk::DirEntry;
|
||||
use std::cmp::Ordering;
|
||||
use std::collections::HashMap;
|
||||
use std::collections::HashSet;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use std::fs::{self, ReadDir};
|
||||
use std::io;
|
||||
|
||||
use dust::Node;
|
||||
use jwalk::WalkDir;
|
||||
|
||||
mod platform;
|
||||
use self::platform::*;
|
||||
|
||||
pub fn get_dir_tree(filenames: &Vec<&str>, apparent_size: bool) -> (bool, Vec<Node>) {
|
||||
let mut permissions = true;
|
||||
let mut results = vec![];
|
||||
for &b in filenames {
|
||||
let mut new_name = String::from(b);
|
||||
while new_name.chars().last() == Some('/') && new_name.len() != 1 {
|
||||
new_name.pop();
|
||||
}
|
||||
let (hp, data) = examine_dir_str(&new_name, apparent_size);
|
||||
permissions = permissions && hp;
|
||||
results.push(data);
|
||||
}
|
||||
(permissions, results)
|
||||
#[derive(Debug, Default, Eq)]
|
||||
pub struct Node {
|
||||
pub name: PathBuf,
|
||||
pub size: u64,
|
||||
pub children: Vec<Node>,
|
||||
}
|
||||
|
||||
fn examine_dir_str(loc: &str, apparent_size: bool) -> (bool, Node) {
|
||||
let mut inodes: HashSet<(u64, u64)> = HashSet::new();
|
||||
let (hp, result) = examine_dir(fs::read_dir(loc), apparent_size, &mut inodes);
|
||||
|
||||
// This needs to be folded into the below recursive call somehow
|
||||
let new_size = result.iter().fold(0, |a, b| a + b.size());
|
||||
(hp, Node::new(loc, new_size, result))
|
||||
impl Ord for Node {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
if self.size == other.size {
|
||||
self.name.cmp(&other.name)
|
||||
} else {
|
||||
self.size.cmp(&other.size)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn examine_dir(
|
||||
a_dir: io::Result<ReadDir>,
|
||||
impl PartialOrd for Node {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Node {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.name == other.name && self.size == other.size && self.children == other.children
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_a_parent_of<P: AsRef<Path>>(parent: P, child: P) -> bool {
|
||||
let parent = parent.as_ref();
|
||||
let child = child.as_ref();
|
||||
child.starts_with(parent) && !parent.starts_with(child)
|
||||
}
|
||||
|
||||
pub fn simplify_dir_names<P: AsRef<Path>>(filenames: Vec<P>) -> HashSet<PathBuf> {
|
||||
let mut top_level_names: HashSet<PathBuf> = HashSet::with_capacity(filenames.len());
|
||||
let mut to_remove: Vec<PathBuf> = Vec::with_capacity(filenames.len());
|
||||
|
||||
for t in filenames {
|
||||
let top_level_name = normalize_path(t);
|
||||
let mut can_add = true;
|
||||
|
||||
for tt in top_level_names.iter() {
|
||||
if is_a_parent_of(&top_level_name, tt) {
|
||||
to_remove.push(tt.to_path_buf());
|
||||
} else if is_a_parent_of(tt, &top_level_name) {
|
||||
can_add = false;
|
||||
}
|
||||
}
|
||||
to_remove.sort_unstable();
|
||||
top_level_names.retain(|tr| to_remove.binary_search(tr).is_err());
|
||||
to_remove.clear();
|
||||
if can_add {
|
||||
top_level_names.insert(top_level_name);
|
||||
}
|
||||
}
|
||||
|
||||
top_level_names
|
||||
}
|
||||
|
||||
pub fn get_dir_tree<P: AsRef<Path>>(
|
||||
top_level_names: &HashSet<P>,
|
||||
ignore_directories: &Option<Vec<PathBuf>>,
|
||||
apparent_size: bool,
|
||||
limit_filesystem: bool,
|
||||
threads: Option<usize>,
|
||||
) -> (bool, HashMap<PathBuf, u64>) {
|
||||
let mut permissions = 0;
|
||||
let mut data: HashMap<PathBuf, u64> = HashMap::new();
|
||||
let restricted_filesystems = if limit_filesystem {
|
||||
get_allowed_filesystems(top_level_names)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let mut examine_dir_args = ExamineDirMutArsg {
|
||||
data: &mut data,
|
||||
file_count_no_permission: &mut permissions,
|
||||
};
|
||||
for b in top_level_names.iter() {
|
||||
examine_dir(
|
||||
b,
|
||||
apparent_size,
|
||||
&restricted_filesystems,
|
||||
ignore_directories,
|
||||
threads,
|
||||
&mut examine_dir_args,
|
||||
);
|
||||
}
|
||||
(permissions == 0, data)
|
||||
}
|
||||
|
||||
fn get_allowed_filesystems<P: AsRef<Path>>(top_level_names: &HashSet<P>) -> Option<HashSet<u64>> {
|
||||
let mut limit_filesystems: HashSet<u64> = HashSet::new();
|
||||
for file_name in top_level_names.iter() {
|
||||
if let Ok(a) = get_filesystem(file_name) {
|
||||
limit_filesystems.insert(a);
|
||||
}
|
||||
}
|
||||
Some(limit_filesystems)
|
||||
}
|
||||
|
||||
pub fn normalize_path<P: AsRef<Path>>(path: P) -> PathBuf {
|
||||
// normalize path ...
|
||||
// 1. removing repeated separators
|
||||
// 2. removing interior '.' ("current directory") path segments
|
||||
// 3. removing trailing extra separators and '.' ("current directory") path segments
|
||||
// * `Path.components()` does all the above work; ref: <https://doc.rust-lang.org/std/path/struct.Path.html#method.components>
|
||||
// 4. changing to os preferred separator (automatically done by recollecting components back into a PathBuf)
|
||||
path.as_ref().components().collect::<PathBuf>()
|
||||
}
|
||||
|
||||
struct ExamineDirMutArsg<'a> {
|
||||
data: &'a mut HashMap<PathBuf, u64>,
|
||||
file_count_no_permission: &'a mut u64,
|
||||
}
|
||||
|
||||
fn examine_dir<P: AsRef<Path>>(
|
||||
top_dir: P,
|
||||
apparent_size: bool,
|
||||
filesystems: &Option<HashSet<u64>>,
|
||||
ignore_directories: &Option<Vec<PathBuf>>,
|
||||
threads: Option<usize>,
|
||||
mut_args: &mut ExamineDirMutArsg,
|
||||
) {
|
||||
let top_dir = top_dir.as_ref();
|
||||
let mut inodes: HashSet<(u64, u64)> = HashSet::new();
|
||||
let mut iter = WalkDir::new(top_dir)
|
||||
.preload_metadata(true)
|
||||
.skip_hidden(false);
|
||||
if let Some(threads_to_start) = threads {
|
||||
iter = iter.num_threads(threads_to_start);
|
||||
}
|
||||
|
||||
'entry: for entry in iter {
|
||||
if let Ok(e) = entry {
|
||||
let maybe_size_and_inode = get_metadata(&e, apparent_size);
|
||||
|
||||
if let Some(dirs) = ignore_directories {
|
||||
let path = e.path();
|
||||
let parts = path.components().collect::<Vec<std::path::Component>>();
|
||||
for d in dirs {
|
||||
let seq = d.components().collect::<Vec<std::path::Component>>();
|
||||
if parts
|
||||
.windows(seq.len())
|
||||
.any(|window| window.iter().collect::<PathBuf>() == *d)
|
||||
{
|
||||
continue 'entry;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
match maybe_size_and_inode {
|
||||
Some(data) => {
|
||||
let (size, inode_device) = data;
|
||||
if !should_ignore_file(apparent_size, filesystems, &mut inodes, inode_device) {
|
||||
process_file_with_size_and_inode(top_dir, mut_args.data, e, size)
|
||||
}
|
||||
}
|
||||
None => *mut_args.file_count_no_permission += 1,
|
||||
}
|
||||
} else {
|
||||
*mut_args.file_count_no_permission += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn should_ignore_file(
|
||||
apparent_size: bool,
|
||||
restricted_filesystems: &Option<HashSet<u64>>,
|
||||
inodes: &mut HashSet<(u64, u64)>,
|
||||
) -> (bool, Vec<Node>) {
|
||||
let mut result = vec![];
|
||||
let mut have_permission = true;
|
||||
maybe_inode_device: Option<(u64, u64)>,
|
||||
) -> bool {
|
||||
match maybe_inode_device {
|
||||
None => false,
|
||||
Some(data) => {
|
||||
let (inode, device) = data;
|
||||
// Ignore files on different devices (if flag applied)
|
||||
if let Some(rs) = restricted_filesystems {
|
||||
if !rs.contains(&device) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if a_dir.is_ok() {
|
||||
let paths = a_dir.unwrap();
|
||||
for dd in paths {
|
||||
match dd {
|
||||
Ok(d) => {
|
||||
let file_type = d.file_type().ok();
|
||||
let maybe_size_and_inode = get_metadata(&d, apparent_size);
|
||||
|
||||
match (file_type, maybe_size_and_inode) {
|
||||
(Some(file_type), Some((size, inode))) => {
|
||||
let s = d.path().to_string_lossy().to_string();
|
||||
if !apparent_size {
|
||||
if let Some(inode_dev_pair) = inode {
|
||||
if inodes.contains(&inode_dev_pair) {
|
||||
// Ignore files already visited or symlinked
|
||||
if inodes.contains(&(inode, device)) {
|
||||
return true;
|
||||
}
|
||||
inodes.insert((inode, device));
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn process_file_with_size_and_inode<P: AsRef<Path>>(
|
||||
top_dir: P,
|
||||
data: &mut HashMap<PathBuf, u64>,
|
||||
e: DirEntry,
|
||||
size: u64,
|
||||
) {
|
||||
let top_dir = top_dir.as_ref();
|
||||
// This path and all its parent paths have their counter incremented
|
||||
for path in e.path().ancestors() {
|
||||
// This is required due to bug in Jwalk that adds '/' to all sub dir lists
|
||||
// see: https://github.com/jessegrosjean/jwalk/issues/13
|
||||
if path.to_string_lossy() == "/" && top_dir.to_string_lossy() != "/" {
|
||||
continue;
|
||||
}
|
||||
inodes.insert(inode_dev_pair);
|
||||
}
|
||||
}
|
||||
|
||||
if d.path().is_dir() && !file_type.is_symlink() {
|
||||
let (hp, recursive) =
|
||||
examine_dir(fs::read_dir(d.path()), apparent_size, inodes);
|
||||
have_permission = have_permission && hp;
|
||||
let new_size = recursive.iter().fold(size, |a, b| a + b.size());
|
||||
result.push(Node::new(s, new_size, recursive))
|
||||
} else {
|
||||
result.push(Node::new(s, size, vec![]))
|
||||
}
|
||||
}
|
||||
(_, None) => have_permission = false,
|
||||
(_, _) => (),
|
||||
}
|
||||
}
|
||||
Err(_) => (),
|
||||
}
|
||||
}
|
||||
} else {
|
||||
have_permission = false;
|
||||
}
|
||||
(have_permission, result)
|
||||
}
|
||||
|
||||
// We start with a list of root directories - these must be the biggest folders
|
||||
// We then repeadedly merge in the children of the biggest directory - Each iteration
|
||||
// the next biggest directory's children are merged in.
|
||||
pub fn find_big_ones<'a>(l: &'a Vec<Node>, max_to_show: usize) -> Vec<&Node> {
|
||||
let mut new_l: Vec<&Node> = l.iter().map(|a| a).collect();
|
||||
new_l.sort();
|
||||
|
||||
for processed_pointer in 0..max_to_show {
|
||||
if new_l.len() == processed_pointer {
|
||||
let s = data.entry(normalize_path(path)).or_insert(0);
|
||||
*s += size;
|
||||
if path.starts_with(top_dir) && top_dir.starts_with(path) {
|
||||
break;
|
||||
}
|
||||
// Must be a list of pointers into new_l otherwise b_list will go out of scope
|
||||
// when it is deallocated
|
||||
let mut b_list: Vec<&Node> = new_l[processed_pointer]
|
||||
.children()
|
||||
.iter()
|
||||
.map(|a| a)
|
||||
.collect();
|
||||
new_l.extend(b_list);
|
||||
new_l.sort();
|
||||
}
|
||||
}
|
||||
|
||||
if new_l.len() > max_to_show {
|
||||
new_l[0..max_to_show + 1].to_vec()
|
||||
pub fn sort_by_size_first_name_second(a: &(PathBuf, u64), b: &(PathBuf, u64)) -> Ordering {
|
||||
let result = b.1.cmp(&a.1);
|
||||
if result == Ordering::Equal {
|
||||
a.0.cmp(&b.0)
|
||||
} else {
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sort(data: HashMap<PathBuf, u64>) -> Vec<(PathBuf, u64)> {
|
||||
let mut new_l: Vec<(PathBuf, u64)> = data.iter().map(|(a, b)| (a.clone(), *b)).collect();
|
||||
new_l.sort_unstable_by(sort_by_size_first_name_second);
|
||||
new_l
|
||||
}
|
||||
|
||||
pub fn find_big_ones(new_l: Vec<(PathBuf, u64)>, max_to_show: usize) -> Vec<(PathBuf, u64)> {
|
||||
if max_to_show > 0 && new_l.len() > max_to_show {
|
||||
new_l[0..max_to_show].to_vec()
|
||||
} else {
|
||||
new_l
|
||||
}
|
||||
}
|
||||
|
||||
fn depth_of_path(name: &PathBuf) -> usize {
|
||||
// Filter required as paths can have some odd preliminary
|
||||
// ("Prefix") bits (for example, from windows, "\\?\" or "\\UNC\")
|
||||
name.components()
|
||||
.filter(|&c| match c {
|
||||
std::path::Component::Prefix(_) => false,
|
||||
_ => true,
|
||||
})
|
||||
.count()
|
||||
}
|
||||
|
||||
pub fn trim_deep_ones(
|
||||
input: Vec<(PathBuf, u64)>,
|
||||
max_depth: u64,
|
||||
top_level_names: &HashSet<PathBuf>,
|
||||
) -> Vec<(PathBuf, u64)> {
|
||||
let mut result: Vec<(PathBuf, u64)> = Vec::with_capacity(input.len() * top_level_names.len());
|
||||
|
||||
for name in top_level_names {
|
||||
let my_max_depth = depth_of_path(name) + max_depth as usize;
|
||||
|
||||
for &(ref k, ref v) in input.iter() {
|
||||
if k.starts_with(name) && depth_of_path(k) <= my_max_depth {
|
||||
result.push((k.clone(), *v));
|
||||
}
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
mod tests {
|
||||
#[allow(unused_imports)]
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_simplify_dir() {
|
||||
let mut correct = HashSet::new();
|
||||
correct.insert(PathBuf::from("a"));
|
||||
assert_eq!(simplify_dir_names(vec!["a"]), correct);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_simplify_dir_rm_subdir() {
|
||||
let mut correct = HashSet::new();
|
||||
correct.insert(["a", "b"].iter().collect::<PathBuf>());
|
||||
assert_eq!(simplify_dir_names(vec!["a/b", "a/b/c", "a/b/d/f"]), correct);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_simplify_dir_duplicates() {
|
||||
let mut correct = HashSet::new();
|
||||
correct.insert(["a", "b"].iter().collect::<PathBuf>());
|
||||
correct.insert(PathBuf::from("c"));
|
||||
assert_eq!(
|
||||
simplify_dir_names(vec![
|
||||
"a/b",
|
||||
"a/b//",
|
||||
"a/././b///",
|
||||
"c",
|
||||
"c/",
|
||||
"c/.",
|
||||
"c/././",
|
||||
"c/././."
|
||||
]),
|
||||
correct
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn test_simplify_dir_rm_subdir_and_not_substrings() {
|
||||
let mut correct = HashSet::new();
|
||||
correct.insert(PathBuf::from("b"));
|
||||
correct.insert(["c", "a", "b"].iter().collect::<PathBuf>());
|
||||
correct.insert(["a", "b"].iter().collect::<PathBuf>());
|
||||
assert_eq!(simplify_dir_names(vec!["a/b", "c/a/b/", "b"]), correct);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_simplify_dir_dots() {
|
||||
let mut correct = HashSet::new();
|
||||
correct.insert(PathBuf::from("src"));
|
||||
assert_eq!(simplify_dir_names(vec!["src/."]), correct);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_simplify_dir_substring_names() {
|
||||
let mut correct = HashSet::new();
|
||||
correct.insert(PathBuf::from("src"));
|
||||
correct.insert(PathBuf::from("src_v2"));
|
||||
assert_eq!(simplify_dir_names(vec!["src/", "src_v2"]), correct);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_a_parent_of() {
|
||||
assert!(is_a_parent_of("/usr", "/usr/andy"));
|
||||
assert!(is_a_parent_of("/usr", "/usr/andy/i/am/descendant"));
|
||||
assert!(!is_a_parent_of("/usr", "/usr/."));
|
||||
assert!(!is_a_parent_of("/usr", "/usr/"));
|
||||
assert!(!is_a_parent_of("/usr", "/usr"));
|
||||
assert!(!is_a_parent_of("/usr/", "/usr"));
|
||||
assert!(!is_a_parent_of("/usr/andy", "/usr"));
|
||||
assert!(!is_a_parent_of("/usr/andy", "/usr/sibling"));
|
||||
assert!(!is_a_parent_of("/usr/folder", "/usr/folder_not_a_child"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_a_parent_of_root() {
|
||||
assert!(is_a_parent_of("/", "/usr/andy"));
|
||||
assert!(is_a_parent_of("/", "/usr"));
|
||||
assert!(!is_a_parent_of("/", "/"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_should_ignore_file() {
|
||||
let mut files = HashSet::new();
|
||||
files.insert((10, 20));
|
||||
|
||||
assert!(!should_ignore_file(true, &None, &mut files, Some((0, 0))));
|
||||
|
||||
// New file is not known it will be inserted to the hashmp and should not be ignored
|
||||
assert!(!should_ignore_file(
|
||||
false,
|
||||
&None,
|
||||
&mut files,
|
||||
Some((11, 12))
|
||||
));
|
||||
assert!(files.contains(&(11, 12)));
|
||||
|
||||
// The same file will be ignored the second time
|
||||
assert!(should_ignore_file(false, &None, &mut files, Some((11, 12))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_should_ignore_file_on_different_device() {
|
||||
let mut files = HashSet::new();
|
||||
files.insert((10, 20));
|
||||
|
||||
let mut devices = HashSet::new();
|
||||
devices.insert(99);
|
||||
let od = Some(devices);
|
||||
|
||||
// If we are looking at a different device (disk) and the device flag is set
|
||||
// then apparent_size is irrelevant - we ignore files on other devices
|
||||
assert!(should_ignore_file(false, &od, &mut files, Some((11, 12))));
|
||||
assert!(should_ignore_file(true, &od, &mut files, Some((11, 12))));
|
||||
|
||||
// We do not ignore files on the same device
|
||||
assert!(!should_ignore_file(false, &od, &mut files, Some((2, 99))));
|
||||
assert!(!should_ignore_file(true, &od, &mut files, Some((2, 99))));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,72 +1,142 @@
|
||||
use std;
|
||||
use jwalk::DirEntry;
|
||||
#[allow(unused_imports)]
|
||||
use std::fs;
|
||||
use std::io;
|
||||
use std::path::Path;
|
||||
|
||||
#[cfg(target_family = "unix")]
|
||||
fn get_block_size() -> u64 {
|
||||
// All os specific implementations of MetatdataExt 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_os = "linux")]
|
||||
pub fn get_metadata(
|
||||
d: &std::fs::DirEntry,
|
||||
use_apparent_size: bool,
|
||||
) -> Option<(u64, Option<(u64, u64)>)> {
|
||||
use std::os::linux::fs::MetadataExt;
|
||||
match d.metadata().ok() {
|
||||
Some(md) => {
|
||||
let inode = Some((md.st_ino(), md.st_dev()));
|
||||
if use_apparent_size {
|
||||
Some((md.len(), inode))
|
||||
} else {
|
||||
Some((md.st_blocks() * get_block_size(), inode))
|
||||
}
|
||||
}
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "unix")]
|
||||
pub fn get_metadata(
|
||||
d: &std::fs::DirEntry,
|
||||
use_apparent_size: bool,
|
||||
) -> Option<(u64, Option<(u64, u64)>)> {
|
||||
#[cfg(target_family = "unix")]
|
||||
pub fn get_metadata(d: &DirEntry, use_apparent_size: bool) -> Option<(u64, Option<(u64, u64)>)> {
|
||||
use std::os::unix::fs::MetadataExt;
|
||||
match d.metadata().ok() {
|
||||
Some(md) => {
|
||||
let inode = Some((md.ino(), md.dev()));
|
||||
d.metadata.as_ref().unwrap().as_ref().ok().map(|md| {
|
||||
if use_apparent_size {
|
||||
Some((md.len(), inode))
|
||||
(md.len(), Some((md.ino(), md.dev())))
|
||||
} else {
|
||||
Some((md.blocks() * get_block_size(), inode))
|
||||
(md.blocks() * get_block_size(), Some((md.ino(), md.dev())))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(target_family = "windows")]
|
||||
pub fn get_metadata(d: &DirEntry, _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 winapi_util::Handle;
|
||||
fn handle_from_path_limited<P: AsRef<Path>>(path: P) -> io::Result<Handle> {
|
||||
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: &DirEntry) -> Option<(u64, Option<(u64, u64)>)> {
|
||||
use winapi_util::file::information;
|
||||
|
||||
let h = handle_from_path_limited(d.path()).ok()?;
|
||||
let info = information(&h).ok()?;
|
||||
|
||||
Some((
|
||||
info.file_size(),
|
||||
Some((info.file_index(), info.volume_serial_number())),
|
||||
))
|
||||
}
|
||||
|
||||
match d.metadata {
|
||||
Some(Ok(ref md)) => {
|
||||
use std::os::windows::fs::MetadataExt;
|
||||
const FILE_ATTRIBUTE_ARCHIVE: u32 = 0x20u32;
|
||||
const FILE_ATTRIBUTE_READONLY: u32 = 0x1u32;
|
||||
const FILE_ATTRIBUTE_HIDDEN: u32 = 0x2u32;
|
||||
const FILE_ATTRIBUTE_SYSTEM: u32 = 0x4u32;
|
||||
const FILE_ATTRIBUTE_NORMAL: u32 = 0x80u32;
|
||||
const FILE_ATTRIBUTE_DIRECTORY: u32 = 0x10u32;
|
||||
|
||||
let attr_filtered = md.file_attributes()
|
||||
& !(FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM);
|
||||
if attr_filtered == FILE_ATTRIBUTE_ARCHIVE
|
||||
|| attr_filtered == FILE_ATTRIBUTE_DIRECTORY
|
||||
|| md.file_attributes() == FILE_ATTRIBUTE_NORMAL
|
||||
{
|
||||
Some((md.len(), None))
|
||||
} else {
|
||||
get_metadata_expensive(&d)
|
||||
}
|
||||
}
|
||||
None => None,
|
||||
_ => get_metadata_expensive(&d),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
pub fn get_metadata(
|
||||
d: &std::fs::DirEntry,
|
||||
use_apparent_size: bool,
|
||||
) -> Option<(u64, Option<(u64, u64)>)> {
|
||||
use std::os::macos::fs::MetadataExt;
|
||||
match d.metadata().ok() {
|
||||
Some(md) => {
|
||||
let inode = Some((md.st_ino(), md.st_dev()));
|
||||
if use_apparent_size {
|
||||
Some((md.len(), inode))
|
||||
} else {
|
||||
Some((md.st_blocks() * get_block_size(), inode))
|
||||
}
|
||||
}
|
||||
None => None,
|
||||
}
|
||||
#[cfg(target_family = "unix")]
|
||||
pub fn get_filesystem<P: AsRef<Path>>(file_path: P) -> Result<u64, io::Error> {
|
||||
use std::os::unix::fs::MetadataExt;
|
||||
let metadata = fs::metadata(file_path)?;
|
||||
Ok(metadata.dev())
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "linux", target_os = "unix", target_os = "macos")))]
|
||||
pub fn get_metadata(d: &std::fs::DirEntry, _apparent: bool) -> Option<(u64, Option<(u64, u64)>)> {
|
||||
match d.metadata().ok() {
|
||||
Some(md) => Some((md.len(), None)),
|
||||
None => None,
|
||||
}
|
||||
#[cfg(target_family = "windows")]
|
||||
pub fn get_filesystem<P: AsRef<Path>>(file_path: P) -> Result<u64, io::Error> {
|
||||
use winapi_util::file::information;
|
||||
use winapi_util::Handle;
|
||||
|
||||
let h = Handle::from_path_any(file_path)?;
|
||||
let info = information(&h)?;
|
||||
Ok(info.volume_serial_number())
|
||||
}
|
||||
|
||||
267
tests/tests.rs
Normal file
267
tests/tests.rs
Normal file
@@ -0,0 +1,267 @@
|
||||
mod tests_symlinks;
|
||||
|
||||
// File sizes differ on both platform and on the format of the disk.
|
||||
// We can at least test the file names are there
|
||||
#[test]
|
||||
pub fn test_basic_output() {
|
||||
assert_cli::Assert::main_binary()
|
||||
.with_args(&["src/test_dir/"])
|
||||
.stdout()
|
||||
.contains(" ┌─┴ ")
|
||||
.stdout()
|
||||
.contains("test_dir ")
|
||||
.stdout()
|
||||
.contains(" ┌─┴ ")
|
||||
.stdout()
|
||||
.contains("many ")
|
||||
.stdout()
|
||||
.contains(" ├── ")
|
||||
.stdout()
|
||||
.contains("hello_file")
|
||||
.stdout()
|
||||
.contains(" ┌── ")
|
||||
.stdout()
|
||||
.contains("a_file ")
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
// fix! [rivy; 2020-22-01] "windows" result data can vary by host (size seems to be variable by one byte); fix code vs test and re-enable
|
||||
#[cfg_attr(target_os = "windows", ignore)]
|
||||
#[test]
|
||||
pub fn test_main_basic() {
|
||||
// -c is no color mode - This makes testing much simpler
|
||||
assert_cli::Assert::main_binary()
|
||||
.with_args(&["-c", "src/test_dir"])
|
||||
.stdout()
|
||||
.is(main_output().as_str())
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
// fix! [rivy; 2020-22-01] "windows" result data can vary by host (size seems to be variable by one byte); fix code vs test and re-enable
|
||||
#[cfg_attr(target_os = "windows", ignore)]
|
||||
#[test]
|
||||
pub fn test_main_multi_arg() {
|
||||
assert_cli::Assert::main_binary()
|
||||
.with_args(&["-c", "src/test_dir/many/", "src/test_dir/", "src/test_dir"])
|
||||
.stdout()
|
||||
.is(main_output().as_str())
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
fn main_output() -> String {
|
||||
r#"
|
||||
0B ┌── a_file │░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░█ │ 0%
|
||||
4.0K ├── hello_file│██████████████████████████████████████████████ │ 100%
|
||||
4.0K ┌─┴ many │██████████████████████████████████████████████ │ 100%
|
||||
4.0K ┌─┴ test_dir │██████████████████████████████████████████████ │ 100%
|
||||
"#
|
||||
.to_string()
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
fn main_output() -> String {
|
||||
r#"
|
||||
0B ┌── a_file │ ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░█ │ 0%
|
||||
4.0K ├── hello_file│ ░░░░░░░░░░░░░░░████████████████ │ 33%
|
||||
8.0K ┌─┴ many │ ███████████████████████████████ │ 67%
|
||||
12K ┌─┴ test_dir │██████████████████████████████████████████████ │ 100%
|
||||
"#
|
||||
.to_string()
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
fn main_output() -> String {
|
||||
"PRs welcome".to_string()
|
||||
}
|
||||
|
||||
// fix! [rivy; 2020-22-01] "windows" result data can vary by host (size seems to be variable by one byte); fix code vs test and re-enable
|
||||
#[cfg_attr(target_os = "windows", ignore)]
|
||||
#[test]
|
||||
pub fn test_main_long_paths() {
|
||||
assert_cli::Assert::main_binary()
|
||||
.with_args(&["-c", "-p", "src/test_dir"])
|
||||
.stdout()
|
||||
.is(main_output_long_paths().as_str())
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
fn main_output_long_paths() -> String {
|
||||
r#"
|
||||
0B ┌── src/test_dir/many/a_file │░░░░░░░░░░░░░░░░░░░░░░░░░░░█ │ 0%
|
||||
4.0K ├── src/test_dir/many/hello_file│████████████████████████████ │ 100%
|
||||
4.0K ┌─┴ src/test_dir/many │████████████████████████████ │ 100%
|
||||
4.0K ┌─┴ src/test_dir │████████████████████████████ │ 100%
|
||||
"#
|
||||
.to_string()
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
fn main_output_long_paths() -> String {
|
||||
r#"
|
||||
0B ┌── src/test_dir/many/a_file │ ░░░░░░░░░░░░░░░░░░█ │ 0%
|
||||
4.0K ├── src/test_dir/many/hello_file│ ░░░░░░░░░██████████ │ 33%
|
||||
8.0K ┌─┴ src/test_dir/many │ ███████████████████ │ 67%
|
||||
12K ┌─┴ src/test_dir │████████████████████████████ │ 100%
|
||||
"#
|
||||
.to_string()
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
fn main_output_long_paths() -> String {
|
||||
"PRs welcome".to_string()
|
||||
}
|
||||
|
||||
// fix! [rivy; 2020-22-01] "windows" result data can vary by host (size seems to be variable by one byte); fix code vs test and re-enable
|
||||
#[cfg_attr(target_os = "windows", ignore)]
|
||||
#[test]
|
||||
pub fn test_apparent_size() {
|
||||
assert_cli::Assert::main_binary()
|
||||
.with_args(&["-c", "-s", "src/test_dir"])
|
||||
.stdout()
|
||||
.is(output_apparent_size().as_str())
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
fn output_apparent_size() -> String {
|
||||
r#"
|
||||
0B ┌── a_file │ ░░░░░░░░░░░░░░░░░░░░░░░█ │ 0%
|
||||
6B ├── hello_file│ ░░░░░░░░░░░░░░░░░░░░░░░█ │ 0%
|
||||
4.0K ┌─┴ many │ ████████████████████████ │ 50%
|
||||
8.0K ┌─┴ test_dir │██████████████████████████████████████████████ │ 100%
|
||||
"#
|
||||
.to_string()
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
fn output_apparent_size() -> String {
|
||||
r#"
|
||||
0B ┌── a_file │ ░░░░░░░░░░░░░░░░░░░░░░░░░░█ │ 0%
|
||||
6B ├── hello_file│ ░░░░░░░░░░░░░░░░░░░░░░░░░██ │ 3%
|
||||
134B ┌─┴ many │ ███████████████████████████ │ 58%
|
||||
230B ┌─┴ test_dir │██████████████████████████████████████████████ │ 100%
|
||||
"#
|
||||
.to_string()
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
fn output_apparent_size() -> String {
|
||||
"".to_string()
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_reverse_flag() {
|
||||
assert_cli::Assert::main_binary()
|
||||
.with_args(&["-c", "-r", "src/test_dir/"])
|
||||
.stdout()
|
||||
.contains(" └─┬ test_dir ")
|
||||
.stdout()
|
||||
.contains(" └─┬ many ")
|
||||
.stdout()
|
||||
.contains(" ├── hello_file")
|
||||
.stdout()
|
||||
.contains(" └── a_file ")
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_d_flag_works() {
|
||||
// We should see the top level directory but not the sub dirs / files:
|
||||
assert_cli::Assert::main_binary()
|
||||
.with_args(&["-d", "1", "-s", "src/test_dir"])
|
||||
.stdout()
|
||||
.doesnt_contain("hello_file")
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
// Check against directories and files whos names are substrings of each other
|
||||
// fix! [rivy; 2020-22-01] "windows" result data can vary by host (size seems to be variable by one byte); fix code vs test and re-enable
|
||||
#[cfg_attr(target_os = "windows", ignore)]
|
||||
#[test]
|
||||
pub fn test_substring_of_names() {
|
||||
assert_cli::Assert::main_binary()
|
||||
.with_args(&["-c", "src/test_dir2"])
|
||||
.stdout()
|
||||
.is(no_substring_of_names_output().as_str())
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
fn no_substring_of_names_output() -> String {
|
||||
"
|
||||
4.0K ┌── dir_name_clash│ ████████ │ 17%
|
||||
4.0K │ ┌── hello │ ░░░░░░░████████ │ 17%
|
||||
8.0K ├─┴ dir_substring │ ███████████████ │ 33%
|
||||
4.0K │ ┌── hello │ ░░░░░░░████████ │ 17%
|
||||
8.0K ├─┴ dir │ ███████████████ │ 33%
|
||||
24K ┌─┴ test_dir2 │████████████████████████████████████████████ │ 100%
|
||||
"
|
||||
.into()
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
fn no_substring_of_names_output() -> String {
|
||||
"
|
||||
4.0K ┌── hello │ ███████████████ │ 33%
|
||||
4.0K ┌─┴ dir_substring │ ███████████████ │ 33%
|
||||
4.0K ├── dir_name_clash│ ███████████████ │ 33%
|
||||
4.0K │ ┌── hello │ ███████████████ │ 33%
|
||||
4.0K ├─┴ dir │ ███████████████ │ 33%
|
||||
12K ┌─┴ test_dir2 │████████████████████████████████████████████ │ 100%
|
||||
"
|
||||
.into()
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
fn no_substring_of_names_output() -> String {
|
||||
"PRs".into()
|
||||
}
|
||||
|
||||
// fix! [rivy; 2020-22-01] "windows" result data can vary by host (size seems to be variable by one byte); fix code vs test and re-enable
|
||||
#[cfg_attr(target_os = "windows", ignore)]
|
||||
#[test]
|
||||
pub fn test_unicode_directories() {
|
||||
assert_cli::Assert::main_binary()
|
||||
.with_args(&["-c", "src/test_dir3"])
|
||||
.stdout()
|
||||
.is(unicode_dir().as_str())
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
fn unicode_dir() -> String {
|
||||
// The way unicode & asian characters are rendered on the terminal should make this line up
|
||||
"
|
||||
0B ┌── 👩.unicode │ █ │ 0%
|
||||
0B ├── ラウトは難しいです!.japan│ █ │ 0%
|
||||
4.0K ┌─┴ test_dir3 │████████████████████████████████ │ 100%
|
||||
"
|
||||
.into()
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
fn unicode_dir() -> String {
|
||||
"
|
||||
0B ┌── 👩.unicode │ █ │ 0%
|
||||
0B ├── ラウトは難しいです!.japan│ █ │ 0%
|
||||
0B ┌─┴ test_dir3 │ █ │ 0%
|
||||
"
|
||||
.into()
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
fn unicode_dir() -> String {
|
||||
"".into()
|
||||
}
|
||||
|
||||
// Check against directories and files whos names are substrings of each other
|
||||
#[test]
|
||||
pub fn test_ignore_dir() {
|
||||
assert_cli::Assert::main_binary()
|
||||
.with_args(&["-c", "-X", "dir_substring", "src/test_dir2"])
|
||||
.stdout()
|
||||
.doesnt_contain("dir_substring")
|
||||
.unwrap();
|
||||
}
|
||||
119
tests/tests_symlinks.rs
Normal file
119
tests/tests_symlinks.rs
Normal file
@@ -0,0 +1,119 @@
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
use std::panic;
|
||||
use std::path::PathBuf;
|
||||
use std::process::Command;
|
||||
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
|
||||
}
|
||||
|
||||
#[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_name.to_str().unwrap();
|
||||
let c = Command::new("ln")
|
||||
.arg("-s")
|
||||
.arg(file_path_s)
|
||||
.arg(link_name_s)
|
||||
.output();
|
||||
assert!(c.is_ok());
|
||||
|
||||
let c = format!(" ┌── {}", link_name_s);
|
||||
let b = format!(" ├── {}", file_path_s);
|
||||
let a = format!("─┴ {}", dir_s);
|
||||
|
||||
assert_cli::Assert::main_binary()
|
||||
.with_args(&["-p", "-c", &dir_s])
|
||||
.stdout()
|
||||
.contains(a.as_str())
|
||||
.stdout()
|
||||
.contains(b.as_str())
|
||||
.stdout()
|
||||
.contains(c.as_str())
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[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");
|
||||
let link_name_s = link_name.to_str().unwrap();
|
||||
let c = Command::new("ln")
|
||||
.arg(file_path_s)
|
||||
.arg(link_name_s)
|
||||
.output();
|
||||
assert!(c.is_ok());
|
||||
|
||||
let a = format!("─┴ {}", dir_s);
|
||||
let b = format!(" ┌── {}", link_name_s);
|
||||
let b2 = format!(" ┌── {}", file_path_s);
|
||||
|
||||
// Because this is a hard link the file and hard link look identical. Therefore
|
||||
// we cannot guarantee which version will appear first.
|
||||
let result = panic::catch_unwind(|| {
|
||||
assert_cli::Assert::main_binary()
|
||||
.with_args(&["-p", "-c", dir_s])
|
||||
.stdout()
|
||||
.contains(a.as_str())
|
||||
.stdout()
|
||||
.contains(b.as_str())
|
||||
.unwrap();
|
||||
});
|
||||
if result.is_err() {
|
||||
assert_cli::Assert::main_binary()
|
||||
.with_args(&["-p", "-c", dir_s])
|
||||
.stdout()
|
||||
.contains(a.as_str())
|
||||
.stdout()
|
||||
.contains(b2.as_str())
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
#[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_name.to_str().unwrap();
|
||||
|
||||
let c = Command::new("ln")
|
||||
.arg("-s")
|
||||
.arg(dir_s)
|
||||
.arg(link_name_s)
|
||||
.output();
|
||||
assert!(c.is_ok());
|
||||
|
||||
let a = format!("─┬ {}", dir_s);
|
||||
let b = format!(" └── {}", link_name_s);
|
||||
|
||||
assert_cli::Assert::main_binary()
|
||||
.with_args(&["-c", "-r", "-p", dir_s])
|
||||
.stdout()
|
||||
.contains(a.as_str())
|
||||
.stdout()
|
||||
.contains(b.as_str())
|
||||
.unwrap();
|
||||
}
|
||||
Reference in New Issue
Block a user