dfrs-0.0.7/.cargo_vcs_info.json0000644000000001121401107312000120130ustar { "git": { "sha1": "1b341b76df9bcb78e796ef261e1d147754e92100" } } dfrs-0.0.7/.github/workflows/ci.yml010064400017500000144000000027311400013770500153440ustar 00000000000000name: CI on: push: branches: [ master ] pull_request: branches: [ master ] schedule: - cron: '0 0 * * 0' # once a week env: CARGO_TERM_COLOR: always jobs: build: name: CI with ${{ matrix.rust }} on ${{ matrix.os }} for ${{ matrix.arch }} runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: os: [ubuntu-latest] rust: [stable, beta, nightly] arch: - x86_64-unknown-linux-gnu - i686-unknown-linux-musl - armv5te-unknown-linux-gnueabi - armv7-unknown-linux-gnueabihf - aarch64-unknown-linux-gnu - powerpc64le-unknown-linux-gnu - mipsel-unknown-linux-gnu - mips64el-unknown-linux-gnuabi64 - s390x-unknown-linux-gnu steps: - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1 with: toolchain: ${{ matrix.rust }} target: ${{ matrix.arch }} override: true components: rustfmt, clippy - uses: actions-rs/cargo@v1 with: use-cross: true command: build args: --target ${{ matrix.arch }} - uses: actions-rs/cargo@v1 with: use-cross: true command: test args: --target ${{ matrix.arch }} - name: cargo fmt uses: actions-rs/cargo@v1 with: command: fmt args: --all -- --check - name: cargo clippy uses: actions-rs/cargo@v1 with: command: clippy args: -- -D warnings dfrs-0.0.7/.gitignore010064400017500000144000000000271365437236300126340ustar 00000000000000/target **/*.rs.bk *.1 dfrs-0.0.7/Cargo.lock0000644000000233001401107312000077720ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. [[package]] name = "aho-corasick" version = "0.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5" dependencies = [ "memchr", ] [[package]] name = "ansi_term" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" dependencies = [ "winapi", ] [[package]] name = "anyhow" version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "afddf7f520a80dbf76e6f50a35bca42a2331ef227a28b3b6dc5c2e2338d114b1" [[package]] name = "atty" version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ "hermit-abi", "libc", "winapi", ] [[package]] name = "bitflags" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" [[package]] name = "cc" version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c0496836a84f8d0495758516b8621a622beb77c0fed418570e50764093ced48" [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" version = "2.33.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" dependencies = [ "ansi_term", "atty", "bitflags", "strsim", "textwrap", "unicode-width", "vec_map", ] [[package]] name = "colored" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b3616f750b84d8f0de8a58bda93e08e2a81ad3f523089b05f1dffecab48c6cbd" dependencies = [ "atty", "lazy_static", "winapi", ] [[package]] name = "dfrs" version = "0.0.7" dependencies = [ "anyhow", "colored", "env_logger", "lazy_static", "log", "nix", "structopt", "strum", "strum_macros", ] [[package]] name = "env_logger" version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26ecb66b4bdca6c1409b40fb255eefc2bd4f6d135dab3c3124f80ffa2a9661e" dependencies = [ "atty", "humantime", "log", "regex", "termcolor", ] [[package]] name = "heck" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87cbf45460356b7deeb5e3415b5563308c0a9b057c85e12b06ad551f98d0a6ac" dependencies = [ "unicode-segmentation", ] [[package]] name = "hermit-abi" version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c" dependencies = [ "libc", ] [[package]] name = "humantime" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7eb0c4e9c72ee9d69b767adebc5f4788462a3b45624acd919475c92597bcaf4f" [[package]] name = "log" version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" dependencies = [ "cfg-if", ] [[package]] name = "memchr" version = "2.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" [[package]] name = "nix" version = "0.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2ccba0cfe4fdf15982d1674c69b1fd80bad427d293849982668dfe454bd61f2" dependencies = [ "bitflags", "cc", "cfg-if", "libc", ] [[package]] name = "once_cell" version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13bd41f508810a131401606d54ac32a467c97172d74ba7662562ebba5ad07fa0" [[package]] name = "proc-macro-error" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ "proc-macro-error-attr", "proc-macro2", "quote", "syn", "version_check", ] [[package]] name = "proc-macro-error-attr" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ "proc-macro2", "quote", "version_check", ] [[package]] name = "proc-macro2" version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" dependencies = [ "unicode-xid", ] [[package]] name = "quote" version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "991431c3519a3f36861882da93630ce66b52918dcf1b8e2fd66b397fc96f28df" dependencies = [ "proc-macro2", ] [[package]] name = "regex" version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9251239e129e16308e70d853559389de218ac275b515068abc96829d05b948a" dependencies = [ "aho-corasick", "memchr", "regex-syntax", "thread_local", ] [[package]] name = "regex-syntax" version = "0.6.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5eb417147ba9860a96cfe72a0b93bf88fee1744b5636ec99ab20c1aa9376581" [[package]] name = "strsim" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" [[package]] name = "structopt" version = "0.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5277acd7ee46e63e5168a80734c9f6ee81b1367a7d8772a2d765df2a3705d28c" dependencies = [ "clap", "lazy_static", "structopt-derive", ] [[package]] name = "structopt-derive" version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ba9cdfda491b814720b6b06e0cac513d922fc407582032e8706e9f137976f90" dependencies = [ "heck", "proc-macro-error", "proc-macro2", "quote", "syn", ] [[package]] name = "strum" version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7318c509b5ba57f18533982607f24070a55d353e90d4cae30c467cdb2ad5ac5c" [[package]] name = "strum_macros" version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee8bc6b87a5112aeeab1f4a9f7ab634fe6cbefc4850006df31267f4cfb9e3149" dependencies = [ "heck", "proc-macro2", "quote", "syn", ] [[package]] name = "syn" version = "1.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c700597eca8a5a762beb35753ef6b94df201c81cca676604f547495a0d7f0081" dependencies = [ "proc-macro2", "quote", "unicode-xid", ] [[package]] name = "termcolor" version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" dependencies = [ "winapi-util", ] [[package]] name = "textwrap" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" dependencies = [ "unicode-width", ] [[package]] name = "thread_local" version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8208a331e1cb318dd5bd76951d2b8fc48ca38a69f5f4e4af1b6a9f8c6236915" dependencies = [ "once_cell", ] [[package]] name = "unicode-segmentation" version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb0d2e7be6ae3a5fa87eed5fb451aff96f2573d2694942e40543ae0bbe19c796" [[package]] name = "unicode-width" version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" [[package]] name = "unicode-xid" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" [[package]] name = "vec_map" version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" [[package]] name = "version_check" version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" [[package]] name = "winapi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", ] [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" dependencies = [ "winapi", ] [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" dfrs-0.0.7/Cargo.toml0000644000000024621401107312000100230ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies # # If you believe there's an error in this file please file an # issue against the rust-lang/cargo repository. If you're # editing this file be aware that the upstream Cargo.toml # will likely look very different (and much more reasonable) [package] edition = "2018" name = "dfrs" version = "0.0.7" authors = ["anthraxx "] description = "Display file system space usage using graphs and colors" homepage = "https://github.com/anthraxx/dfrs" readme = "README.md" keywords = ["df", "disk", "disk-usage"] categories = ["command-line-utilities"] license = "MIT" repository = "https://github.com/anthraxx/dfrs" [profile.release] lto = true codegen-units = 1 [dependencies.anyhow] version = "1.0" [dependencies.colored] version = "2.0" [dependencies.env_logger] version = "0.8" [dependencies.lazy_static] version = "1.4" [dependencies.log] version = "0.4" [dependencies.nix] version = "0.19" [dependencies.structopt] version = "0.3" [dependencies.strum] version = "0.20" [dependencies.strum_macros] version = "0.20" dfrs-0.0.7/Cargo.toml.orig010064400017500000144000000011631401103444000135100ustar 00000000000000[package] name = "dfrs" version = "0.0.7" description = "Display file system space usage using graphs and colors" authors = ["anthraxx "] edition = "2018" readme = "README.md" license = "MIT" repository = "https://github.com/anthraxx/dfrs" homepage = "https://github.com/anthraxx/dfrs" categories = ["command-line-utilities"] keywords = ["df", "disk", "disk-usage"] [dependencies] nix = "0.19" anyhow = "1.0" colored = "2.0" structopt = "0.3" log = "0.4" env_logger = "0.8" strum = "0.20" strum_macros = "0.20" lazy_static = "1.4" # termcolor = "1.0" [profile.release] lto = true codegen-units = 1 dfrs-0.0.7/LICENSE010064400017500000144000000020711345021604300116340ustar 00000000000000The MIT License (MIT) Copyright (c) 2019 Levente Polyak Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. dfrs-0.0.7/Makefile010064400017500000144000000046551401107277000123020ustar 00000000000000-include Makefile.local DESTDIR ?= PREFIX ?= /usr/local BINDIR ?= ${PREFIX}/bin DATAROOTDIR ?= ${PREFIX}/share MANDIR ?= ${DATAROOTDIR}/man TARBALLDIR ?= target/release/tarball TARBALLFORMAT=tar.gz RM := rm CARGO := cargo SCDOC := scdoc INSTALL := install GIT := git GPG := gpg SED := sed DEBUG := 0 ifeq ($(DEBUG), 0) CARGO_OPTIONS := --release --locked CARGO_TARGET := release else CARGO_OPTIONS := CARGO_TARGET := debug endif .PHONY: all dfrs test docs man completions clean install uninstall all: dfrs test docs dfrs: $(CARGO) build $(CARGO_OPTIONS) test: $(CARGO) test $(CARGO_OPTIONS) lint: $(CARGO) fmt -- --check $(CARGO) check find . -name '*.rs' -exec touch {} + $(CARGO) clippy --all -- -D warnings docs: man completions man: contrib/man/dfrs.1 contrib/man/%: contrib/man/%.scd $(SCDOC) < $^ > $@ completions: dfrs target/$(CARGO_TARGET)/dfrs completions bash | $(INSTALL) -Dm 644 /dev/stdin target/completion/bash/dfrs target/$(CARGO_TARGET)/dfrs completions zsh | $(INSTALL) -Dm 644 /dev/stdin target/completion/zsh/_dfrs target/$(CARGO_TARGET)/dfrs completions fish | $(INSTALL) -Dm 644 /dev/stdin target/completion/fish/dfrs.fish clean: $(RM) -rf target contrib/man/*.1 install: dfrs docs $(INSTALL) -Dm 755 target/$(CARGO_TARGET)/dfrs -t $(DESTDIR)$(BINDIR) $(INSTALL) -Dm 644 contrib/man/*.1 -t $(DESTDIR)$(MANDIR)/man1 $(INSTALL) -Dm 644 target/completion/bash/dfrs -t $(DESTDIR)$(DATAROOTDIR)/bash-completion/completions $(INSTALL) -Dm 644 target/completion/zsh/_dfrs -t $(DESTDIR)$(DATAROOTDIR)/zsh/site-functions $(INSTALL) -Dm 644 target/completion/fish/dfrs.fish -t $(DESTDIR)$(DATAROOTDIR)/fish/vendor_completions.d uninstall: $(RM) -f $(DESTDIR)$(BINDIR)/dfrs $(RM) -f $(DESTDIR)$(MANDIR)/man1/dfrs.1 $(RM) -f $(DESTDIR)$(DATAROOTDIR)/bash-completion/completions/dfrs $(RM) -f $(DESTDIR)$(DATAROOTDIR)/zsh/site-functions/_dfrs $(RM) -f $(DESTDIR)$(DATAROOTDIR)/fish/vendor_completions.d/dfrs.fish release: all $(INSTALL) -d $(TARBALLDIR) @read -p 'version> ' TAG && \ $(SED) "s|version = .*|version = \"$$TAG\"|" -i Cargo.toml && \ $(CARGO) build --release && \ $(GIT) commit --gpg-sign --message "version: release $$TAG" Cargo.toml Cargo.lock && \ $(GIT) tag --sign --message "version: release $$TAG" $$TAG && \ $(GIT) archive -o $(TARBALLDIR)/dfrs-$$TAG.$(TARBALLFORMAT) --format $(TARBALLFORMAT) --prefix=dfrs-$$TAG/ $$TAG && \ $(GPG) --detach-sign $(TARBALLDIR)/dfrs-$$TAG.$(TARBALLFORMAT) dfrs-0.0.7/README.md010064400017500000144000000017141400071466100121120ustar 00000000000000# dfrs ![Build Status](https://img.shields.io/github/workflow/status/anthraxx/dfrs/CI) ![Latest release](https://img.shields.io/github/v/release/anthraxx/dfrs) [![crates.io version](https://img.shields.io/crates/v/dfrs.svg)](https://crates.io/crates/dfrs) ![License](https://img.shields.io/github/license/anthraxx/dfrs) Display file system space usage using graphs and colors ![](contrib/screenshot.png) *dfrs* displays the amount of disk space available on the file system containing each file name argument. If no file name is given, the space available on all currently mounted file systems is shown. *dfrs*(1) is a tool similar to *df*(1) except that it is able to show a graph along with the data and is able to use colors. Without any argument, size is displayed in human-readable format. ## Installation cargo install dfrs ### Arch Linux pacman -S dfrs ### Debian sid/bullseye apt install dfrs ### Alpine apk add dfrs ## License MIT dfrs-0.0.7/contrib/man/dfrs.1.scd010064400017500000144000000033761400105214200146320ustar 00000000000000dfrs(1) # NAME dfrs - Display file system space usage using graphs and colors # SYNOPSIS *dfrs* [_OPTION_]... [_FILE_]... # DESCRIPTION *dfrs* displays the amount of disk space available on the file system containing each file name argument. If no file name is given, the space available on all currently mounted file systems is shown. *dfrs*(1) is a tool similar to *df*(1) except that it is able to show a graph along with the data and is able to use colors. Without any argument, size is displayed in human-readable format. # OPTIONS Show information about the file system on which each _FILE_ resides, or all regular file systems by default. *-a*, *--more* Show more file systems; use *-a* twice to show all *-aa*, *--all* Show all file systems *-c* Force colors even if stdout is not a tty. This is useful with *watch -c* *--color* [_WHEN_] Colorize the output; _WHEN_ can be 'auto' (default if omitted), 'always', or 'never' *-i*, *--inodes* Show inode instead of block usage *-h*, *--human-readable* Print sizes in powers of 1024 (e.g., 1023M) *-H*, *--si* Print sizes in powers of 1000 (e.g., 1.1G) *--total* Produce and show a grand total *-l*, *--local* Limit listing to local file systems *--no-aliases* Do not resolve file system shorthand aliases (e.g., LVM) *--columns* [_COLUMN_]... Display columns defined as a comma separated list; _COLUMN_ can be: filesystem, type, bar, used, used_percentage, available, available_percentage, capacity, mounted_on *--mounts* [_FILE_] File to get mount information from (e.g., /proc/mounts or /etc/mtab) *-V*, *--version* Prints version information *--help* Show a short help text. # BUGS Bugs can be reported on the bug tracker _https://github.com/anthraxx/dfrs/issues_ # AUTHORS Levente Polyak dfrs-0.0.7/contrib/screenshot.png010064400017500000144000000341701375530074000151650ustar 00000000000000PNG  IHDRg iCCPICC profile(}=H@߶JTD,(:Yq*BZu04$).kŪ "%~Zxpw}w^f1eq!]KD1 ub<˻ѭLYs'tAG.q.8#' 6ۘ x83.+8*kޓ0VNs ,b "Ȩ2,hH1U#*P!9~?[3?9&@m]QcngJk+u`ZK=uK`I ɑ4<~FߔnК۷9N4*y^xwW{iHrfbKGD pHYs  tIME   /tEXtCommentCreated with GIMPW IDATxy\TϙaUBQ\R\R(kV὿Z^xyd%fVLrqMe AF_y93<g<9(B!uF@!BH+B!={6K.-YfDEEQXXHddB!j:Sr!q B!]h۶-G!--)S;u%B!5~DŽвeKGVZܼySj^!Bl b{{{ڑ;w$;;{a~޽/?#ӻi&/_ݻCUU 36l ==$Lb̧B!Q۵k=zTT?CEܹs׫[V1˷׾}+VNNNj^Լw_,w6mRsss~&f̘/榆?Yy%/yK^׃C<󉍍5+jvss# #Fۉ|ժUlܸB ((( ==?P~!Bhٲe L0{{{8@DDNNNfeIIIw .|֬Yٳ~wfΜB!0/荋_?={DFtt4'Nt'%qeHU*rNN~!<쳼 I !BlPUUQU^ob)))ݻS3},_ :GGG_>҂B!¼V6l@Q\B=;wY; ۛK.1m4֮]kь~ߟx֭[/B!D W !BQgɍnB!B!BH+B!B!BH+B!]eU 6-,5Y(!mXsv{A?wR⎃ބk]~hH5hHRB!##D z=H2K VzE[GH:8v=>;(-V_8hOeW'/P4ZЕ@AN}~N:(fu8rY??%ht4hGd%⾕<+]|d/xĪh&9z?GwrGSpT׌ ׌S(|+7 {QhոUQNa^Fۣh䤛OUp*qe hY$~n[;Ϳ .}QנVG8#a(Y7I:C뱲f{/I_iԺ'!ܯ @U(J_jGCWVN& 򮃪V~(+)guϙH٨P ýK4i߇KV'Gֳ5gpnV礑rͻaҔQoy5ϽɅ~. }M<ӠE޾5nç_ght$b4̲Y"t u6p6kIjAi|P5|r,D¼lsqjօ[ vWvkhɻ3M{Vz@aֳEƪKĵSiؼ>zji9X8`mܜ@V=쉮#nLTQng/"r~+;5p *)I哸?m=[=P xAr褊rlFgÑq"f944~./|qVRx:έ5Z?ioM}~"vMZ)X̵m/r7{-py;Wvz2.DH?;`vϓ~>K85Gk[f&suAuל 1{Z?EQ~.kѴv[Ί߳~q*zd]>ApvWvmM{Qeff5&>/3+i`emgrufe *?iDtLsIȸɈWe??v-§X:ΗDhi"r?٣d]5ź~)L|rީ~b٩\=W&~W4:V6vd' ~7Xr%v^֥)|'ǒxK*kFz`##KVtv E5Ƴnųgd' BT/N|P|rn?)\Ouk >hќo7;;6|_YU,#z{@,ߣRVUUٶm̞=솬8qcǎ5%yfE1;Ν;s1|9rõx+ S `< 8-׫Kj4 4ήA);C-wi D #u|n qW^ƍW&Yz5 xyyG1`K,}tޝ֭[cooϜ9ߤG̙3߿tdcp6nHvvv雳Uz*^^^| 8Y7779vqٱch׮斿СC;w. 6DQ֯_… iԨm۶駟^3nWǏ( =III((ݐvv6 bƌoߞAѧOFfXlǏP ͛&|a\ &ů-.`&\>t|ƍDk779Fƍ-V_Y+ba?k芃λa76_ezGMBBv2  !##۷a\/̄ ڵkL>Ç/0l00'ʶoҤ O>$|DFFe%ׯ_gƌ\~Qۥ_bժUlܸ\2Vˀ !%%SNj,e˜f,\!Cfv'O&**Q#(W (AWo-yx)O7ɷP4 pY[ߦosFŷ6\8"e(政2c _r?(PV}`c)_mj_!,j4&NXWVd㲤_M =j<5~aSV^O?+={Dӱm6I%ۗ)SPfK\Piܸ1VVVe/We˜7Nu2{mڴ!((I&#8*> s`~]g0\QF_(׀o0\\ί-,_;>1 lYn>_`8M n}ߢ:R~BX4۷/666|$ؽ5H4x18.95^ 11}1d^y2o~eۗ˫]|t1;}c}jիW),,4IӘ֗(RF4ϟOTT 6d„ es"-- UU UU,xzCGb+ }5 s-ouϟϗKߵ "&&ZVja^N 0:]|qֹȨ݁Wa_|h̼y)))ݻS3}322oXx1>>>888K/d1j(XԆۥ_)))ٳٳ'{6 Txwhڴ)]taĈXJNQQ}SN___Əo[IIIұc*oN9^z1fʴi֭Y釅҃&33EQHy};u;/^|G8pz^~"y(ۀEd"Qnn."!Vٜ;G俢WV )yaѿ8p X9< n6ߝBQ@vw<@tӦMcڵ&냂'22\~zr 4YWYm?b7nLbb"!!!lذI&q!bbbغu+/6]9WdܸqdddǞ={駟5kN<ɂ عsgPQʰa 66`qqqHBe0t$08R. |YaK?H0Nr>(ž};VXm/[MVE巄E&c 4S SzNǷe43_GWگ2E۰aO߿'tk֬!&&3fܓE)y qcG&qWh˱c5تX"Çظq#~~~l޼'x̼ݻW^ z5 Q.ɉyѯ_?RRR6mk֬W^ zBC\edd0zhiQ!BQFB!v4RB! gɉ__K-U魞}NoFBOFzB!ă.Ze˖ݳY:}[[[V\IVVW^e59zEbb=ssҷ&.. 9zk?K?3{zmߢEsݸ_fZs/VMa[boZ_/1{˦~~6mA^@lL~ Y|u5 5bȑtdn?sLՕ[ry/忟}G&!NX!* ; UO|{~hF*;8qZz!:D-  |<`;p- ;9 OW`ϕmIWG!' eϜh FLg$.]5|C{à`8qz_ 8t2')7Hbcc|||عs'ݻm %!!TVX:t 77{{{;wLNNvvvoر~gVZe( ̙3x<ʕ+5je˖ĐIxx8u...]TO6km6<==QUʏ8qcǎ5%yfE1;Ν;sϕyVM ǖ_j.M^E7hGQhРvv ypȃ)غtC Nu #}a!@˻9'_n.xG8xjz˩@UJ{u[}% _mEQfƆq1tPW 06mDXXX/%Kpww{d̙CPPG̙3ߟ+W'mܸJ7gUV;v$""p 7779vqǎ#00sH>}HIIa̙3W_} ҰaCڶm#?i?~8ЫW/+ܐvv6 "22ӺukCΝQUլu:˖-7Sat녊 xꟺr?{/0ֲxvGCfVo̚1XBޛ>#;;JY/N լ?һ'{*~G< ڶ੻4k -g֐CIDAT&󏛷?gpmΰqa2,_~xw$$$k.2777 !##۷a\/̄ ڵkL>Ç/0l0+۾I&<|ɖ-[LFA_ό3~z̷K`=zf͚ŀh 0RRR8uW̩~m֯_… 2diiif1ydؿ5egk>rn7a@{[٠;x |._mE-z5 'N,3_2399ٸgd7m8"[rj8::$^~WWWzNc۶m~eۻӭKF۷/SL(sE\tdX[[Ӹqcʤ_W9ne۴iCPP&M#F`2Ã4n޼իW),,ӓs/WeKzEr#S'** ___&L@hhYUfdXUUvJttIT6^S"k~ϟHzVS{ZFdEf'h tfAmfDl OK\!e0wϼuY^e0O2oRv:@uWۯv1 f޼yeєԩSqttgO{ddd7߰xb|||pppॗ^bɒ%& cԨQ 8ܩ KSRRسg!!!8;;ӳgOzm;дiStˆ#XbE5j,**cԩY~YoJǎԈȑ#իcƌaСL6nݺ~XX_&33EQ$.G]\5j/b)E1ͩJ'AdࢉS-2\yJ?Щ5ՇޏË= |3w{~ [nbCa^ʧ;A 0 > Y.BWҠ7 v'x{{s%MڵkMOdd$ q}&Y:5kÌ3I*߫W/+9島ȑ#$G@6EC 7T.sfk7oU?G͙5g&nMX|K/9z+VUG9yuQx=3 RhGw [SDVe.9uQh?\KK B|D )iփV=A_vaki7Ho8ð1V<aBKI+eۨQ#FI׮]Mrҟ9s&nݺϛ<~)죏>"55777 !J^/"fJ>sۋ8&4$Z"==Hlb2 pu{?^(үٳgIKK#66/BJJ ƃnٲe?~,X"9oqvv_$--4&OLTTgРArqYsPޙ3gw{AC0z(քs5Z/iia"W|:[|TÁU}ÿنC͞4?-"#'+ugx@ozPZ-"+I%3AsIz޲BS W_Qh4L8iɥ-nڴqDÇqtt4NIXz5O?4NǶm0'ʶwww/[ߗ;88зo_LCQүLAa|{tҥK&kҤ MSƩQQQfM61i$9b8(D-(v~mYdY4h`˚uXg&v"~[>U[' UCZn\;YZ9y`IWƋn-g~6\>Gۯv19틍 &* v9{,Ƒ‹/㔔rJLLd߾} 2N:_SXhzzvW}I8u1\|t111fQ_uW^E~hN̟?(|}}0af//2#êҵkWN?WvJZꬿ^O (ȵ~ϟHzVS{ZFdE5VFUUTzjʌf]R)V;}L Sp n$H+_u\PH?o<.l]t?_mEb3'88y FSRRػw/SNёgy>}gdd7߰xb|||pppॗ^bɒ%& cԨQ 8d>peW}JJ {!$$gggzI޽M0y6mJ.]1b+VRIIIұc;1cжm[4iBpp06lH#S#GW^3C2m4ufVP?KGU{&Pnn."/9s'AKA3w!#^R`ۨ((].QDAZPr4zjk4&sp~*$ZDwQpj%a_}Y[گv1uЮ];?@K.]ÇYv ONdd$NNN޽)S|f|g\x,ʶ1b˗/'11h6l`<0i$/^LLL>ޮ̜O<ɂ عs' 6dΜ9]_5}nnnlٲɓ'[!+???BCCݻ7dddLxx8<)ȁpR?-(J_?5b!v{4˒z܀-oXoiz=vr1y뽻^*WcteB~n(ao@*"% k{WZd~ -@+RO9b |pij{+j;6lӧyIF,5kaƌ$?>3ʃBC,B_qYҧOƏO2aNcƍ / !"EgΜ~,H?""y駟oQBB!j\ B-2A! !BqɝB!B!BH+B!Dmz۶mˑ#GHKK+@ !B!DB˖-ׯZB!u+gj4888H- !B`ڴi\pΝqwwZB!}z}YHOOgJ !BZOϖ-[g„ s"""prrZB!?荋_?={DFtt4'NZB!@vvq(zZԒB!, /// Bypass tty detection and always show colors #[structopt(short = "c", group = "color_group")] pub color_always: bool, /// Show inode instead of block usage #[structopt(short, long)] pub inodes: bool, /// Print sizes in powers of 1024 (e.g., 1023M) #[structopt(short = "h", long = "human-readable", group = "number_format")] pub base2: bool, /// Print sizes in powers of 1000 (e.g., 1.1G) #[structopt(short = "H", long = "si", group = "number_format")] pub base10: bool, /// Produce and show a grand total #[structopt(long)] pub total: bool, /// Limit listing to local file systems #[structopt(short, long)] pub local: bool, /// Do not resolve file system shorthand aliases (e.g., LVM) #[structopt(long)] pub no_aliases: bool, /// File to get mount information from #[structopt(long, parse(from_os_str), default_value = "/proc/self/mounts")] pub mounts: PathBuf, /// Verbose logging #[structopt(short)] pub verbose: bool, #[structopt(parse(from_os_str))] pub paths: Vec, /// Display columns as comma separated list #[structopt(long, use_delimiter = true, possible_values = &ColumnType::VARIANTS, default_value = &COLUMNS_OPT_DEFAULT_VALUE)] pub columns: Vec, #[structopt(subcommand)] pub subcommand: Option, } #[derive(Debug, StructOpt)] pub enum SubCommand { /// Generate shell completions #[structopt(name = "completions")] Completions(Completions), } #[derive(Debug, StructOpt, ToString, EnumString, EnumVariantNames)] #[strum(serialize_all = "lowercase")] pub enum ColorOpt { Auto, Always, Never, } #[derive(Debug, StructOpt, EnumString)] #[strum(serialize_all = "lowercase")] pub enum DisplayFilter { Minimal, More, All, } impl DisplayFilter { pub const fn from_u8(n: u8) -> Self { match n { 0 => Self::Minimal, 1 => Self::More, _ => Self::All, } } pub fn get_mnt_fsname_filter(&self) -> Vec<&'static str> { match self { Self::Minimal => vec!["/dev*", "storage"], Self::More => vec!["dev", "run", "tmpfs", "/dev*", "storage"], Self::All => vec!["*"], } } } #[derive(Debug, StructOpt)] pub enum NumberFormat { Base10, Base2, } impl NumberFormat { pub const fn get_powers_of(&self) -> f64 { match self { Self::Base10 => 1000_f64, Self::Base2 => 1024_f64, } } } #[derive(Debug, StructOpt, ToString, EnumString, EnumVariantNames)] #[strum(serialize_all = "snake_case")] pub enum ColumnType { Filesystem, Type, Bar, Used, UsedPercentage, Available, AvailablePercentage, Capacity, MountedOn, } impl ColumnType { pub const fn label(&self, inodes_mode: bool) -> &str { match self { Self::Filesystem => "Filesystem", Self::Type => "Type", Self::Bar => "", Self::Used => "Used", Self::UsedPercentage => "Used%", Self::Available => "Avail", Self::AvailablePercentage => "Avail%", Self::Capacity => { if inodes_mode { "Inodes" } else { "Size" } } Self::MountedOn => "Mounted on", } } } lazy_static! { static ref COLUMNS_OPT_DEFAULT_VALUE: String = vec![ ColumnType::Filesystem, ColumnType::Type, ColumnType::Bar, ColumnType::UsedPercentage, ColumnType::Available, ColumnType::Used, ColumnType::Capacity, ColumnType::MountedOn ] .iter() .map(|e| e.to_string()) .collect::>() .join(","); } #[derive(Debug, StructOpt)] pub struct Completions { #[structopt(possible_values=&Shell::variants())] pub shell: Shell, } pub fn gen_completions(args: &Completions) -> Result<()> { Args::clap().gen_completions_to("dfrs", args.shell, &mut stdout()); Ok(()) } dfrs-0.0.7/src/errors.rs010064400017500000144000000001331401103444300132710ustar 00000000000000pub use anyhow::{anyhow, Context, Error, Result}; pub use log::{debug, error, info, warn}; dfrs-0.0.7/src/main.rs010064400017500000144000000244211401103444300127070ustar 00000000000000#![deny(clippy::nursery, clippy::cargo)] extern crate anyhow; extern crate strum; extern crate strum_macros; use args::*; mod args; mod errors; use errors::*; mod theme; use theme::Theme; mod mount; use mount::*; mod util; use util::bar; use util::try_print; use std::fs::File; use std::path::Path; use std::path::PathBuf; use nix::sys::statfs; use env_logger::Env; use crate::mount::Mount; use crate::util::format_percentage; use anyhow::Result; use colored::*; use std::io::{stdout, Write}; use structopt::StructOpt; #[inline] fn column_width(mnt: &[Mount], f: F, heading: &str) -> usize where F: Fn(&Mount) -> usize, { mnt.iter() .map(f) .chain(std::iter::once(heading.len())) .max() .unwrap() } fn display_mounts( mnts: &[Mount], theme: &Theme, delimiter: &NumberFormat, inodes_mode: bool, no_aliases: bool, ) { let color_heading = theme.color_heading.unwrap_or(Color::White); let fsname_func = if no_aliases { Mount::fsname } else { Mount::fsname_aliased }; let fsname_width = column_width( mnts, |m| fsname_func(m).len(), ColumnType::Filesystem.label(inodes_mode), ); let type_width = column_width( mnts, |m| m.mnt_type.len(), ColumnType::Type.label(inodes_mode), ); let available_width = column_width( mnts, |m| m.free_formatted(delimiter).len(), ColumnType::Available.label(inodes_mode), ); let used_width = column_width( mnts, |m| m.used_formatted(delimiter).len(), ColumnType::Used.label(inodes_mode), ); let capacity_width = column_width( mnts, |m| m.capacity_formatted(delimiter).len(), ColumnType::Capacity.label(inodes_mode), ); let mounted_width = column_width( mnts, |m| m.mnt_dir.len(), ColumnType::MountedOn.label(inodes_mode), ); let print_heading_left_func = |column: &ColumnType, width: usize| -> String { format!( "{: String { format!( "{:>width$} ", column.label(inodes_mode).color(color_heading), width = width, ) }; let mut line = String::new(); for column in &theme.columns { match column { ColumnType::Filesystem => { line.push_str(print_heading_left_func(column, fsname_width).as_str()); } ColumnType::Type => { line.push_str(print_heading_left_func(column, type_width).as_str()); } ColumnType::Bar => { line.push_str(print_heading_left_func(column, theme.bar_width).as_str()); } ColumnType::Used => { line.push_str(print_heading_right_func(column, used_width).as_str()); } ColumnType::UsedPercentage => { line.push_str(print_heading_right_func(column, 6).as_str()); } ColumnType::Available => { line.push_str(print_heading_right_func(column, available_width).as_str()); } ColumnType::AvailablePercentage => { line.push_str(print_heading_right_func(column, 6).as_str()); } ColumnType::Capacity => { line.push_str(print_heading_right_func(column, capacity_width).as_str()); } ColumnType::MountedOn => { line.push_str(print_heading_left_func(column, mounted_width).as_str()); } } } if try_println!("{}", line.trim_end()).is_err() { return; } for mnt in mnts { let usage_color = mnt.usage_color(theme); let used_percentage = format_percentage(mnt.used_percentage()).color(usage_color); let available_percentage = format_percentage(mnt.free_percentage()).color(usage_color); line.clear(); for column in &theme.columns { match column { ColumnType::Filesystem => { line.push_str( format!("{: { line.push_str( format!("{: { line.push_str( format!( "{: { line.push_str( format!( "{:>width$} ", mnt.used_formatted(delimiter).color(usage_color), width = used_width ) .as_str(), ); } ColumnType::UsedPercentage => { line.push_str(format!("{} ", used_percentage).as_str()); } ColumnType::Available => { line.push_str( format!( "{:>width$} ", mnt.free_formatted(delimiter).color(usage_color), width = available_width ) .as_str(), ); } ColumnType::AvailablePercentage => { line.push_str(format!("{} ", available_percentage).as_str()); } ColumnType::Capacity => { line.push_str( format!( "{:>width$} ", mnt.capacity_formatted(delimiter).color(usage_color), width = capacity_width ) .as_str(), ); } ColumnType::MountedOn => { line.push_str( format!("{: Result<()> { if let Some(color) = args.color { debug!("Bypass tty detection for colors: {:?}", color); match color { ColorOpt::Auto => {} ColorOpt::Always => { colored::control::set_override(true); } ColorOpt::Never => { colored::control::set_override(false); } } } if args.color_always { debug!("Bypass tty detection for colors: always"); colored::control::set_override(true); } match args.subcommand { Some(SubCommand::Completions(completions)) => args::gen_completions(&completions)?, _ => { let mut theme = Theme::new(); theme.columns = args.columns; let delimiter = if args.base10 { NumberFormat::Base10 } else { NumberFormat::Base2 }; let mounts_to_show = if args.all { DisplayFilter::All } else if args.more { DisplayFilter::More } else { DisplayFilter::from_u8(args.display) }; let mut mnts = get_mounts( &mounts_to_show, args.inodes, &args.paths, &args.mounts, args.local, )?; if args.total { mnts.push(util::calc_total(&mnts)); } display_mounts(&mnts, &theme, &delimiter, args.inodes, args.no_aliases); } } Ok(()) } fn get_mounts( mounts_to_show: &DisplayFilter, show_inodes: bool, paths: &[PathBuf], mounts: &Path, local_only: bool, ) -> Result> { let f = File::open(mounts)?; let mut mnts = parse_mounts(f)?; mnts.retain(|mount| { mounts_to_show .get_mnt_fsname_filter() .iter() .any(|fsname| util::mnt_matches_filter(mount, fsname)) }); if local_only { mnts.retain(Mount::is_local); } for mnt in &mut mnts { mnt.statfs = statfs::statfs(&mnt.mnt_dir[..]).ok(); let (capacity, free) = match mnt.statfs { Some(stat) => { if show_inodes { (stat.files() as u64, stat.files_free() as u64) } else { ( stat.blocks() as u64 * (stat.block_size() as u64), stat.blocks_available() as u64 * (stat.block_size() as u64), ) } } None => (0, 0), }; mnt.capacity = capacity; mnt.free = free; mnt.used = capacity - free; } if !paths.is_empty() { let mut out = Vec::new(); for path in paths { let path = match path.canonicalize() { Ok(path) => path, Err(err) => { eprintln!("dfrs: {}: {}", path.display(), err); continue; } }; if let Some(mnt) = util::get_best_mount_match(&path, &mnts) { out.push(mnt.clone()); } } return Ok(out); } mnts.sort_by(util::cmp_by_capacity_and_dir_name); Ok(mnts) } fn main() { let args = Args::from_args(); let logging = if args.verbose { "debug" } else { "info" }; env_logger::init_from_env(Env::default().default_filter_or(logging)); if let Err(err) = run(args) { eprintln!("Error: {}", err); for cause in err.chain().skip(1) { eprintln!("Because: {}", cause); } std::process::exit(1); } } dfrs-0.0.7/src/mount.rs010064400017500000144000000141071401103444300131250ustar 00000000000000use crate::errors::*; use crate::args::NumberFormat; use crate::theme::Theme; use crate::util::{format_count, lvm_alias}; use colored::Color; use std::fs::File; use std::io::BufRead; use std::io::BufReader; #[derive(Clone)] pub struct Mount { pub mnt_fsname: String, pub mnt_dir: String, pub mnt_type: String, pub mnt_opts: String, pub mnt_freq: i32, pub mnt_passno: i32, pub capacity: u64, pub free: u64, pub used: u64, pub statfs: Option, } impl Mount { pub fn fsname(&self) -> String { self.mnt_fsname.clone() } pub fn fsname_aliased(&self) -> String { let lvm = lvm_alias(&self.mnt_fsname); lvm.unwrap_or_else(|| self.mnt_fsname.clone()) } pub fn used_percentage(&self) -> Option { match self.capacity { 0 => None, _ => Some(100.0 - self.free as f32 * 100.0 / self.capacity as f32), } } pub fn free_percentage(&self) -> Option { match self.free { 0 => None, _ => Some(self.free as f32 * 100.0 / self.capacity as f32), } } pub fn capacity_formatted(&self, delimiter: &NumberFormat) -> String { format_count(self.capacity as f64, delimiter.get_powers_of()) } pub fn free_formatted(&self, delimiter: &NumberFormat) -> String { format_count(self.free as f64, delimiter.get_powers_of()) } pub fn used_formatted(&self, delimiter: &NumberFormat) -> String { format_count(self.used as f64, delimiter.get_powers_of()) } pub fn usage_color(&self, theme: &Theme) -> Color { match &self.used_percentage() { Some(p) if p >= &theme.threshold_usage_high => &theme.color_usage_high, Some(p) if p >= &theme.threshold_usage_medium => &theme.color_usage_medium, Some(_) => &theme.color_usage_low, _ => &theme.color_usage_void, } .unwrap_or(Color::White) } #[inline] pub fn is_local(&self) -> bool { !self.is_remote() } pub fn is_remote(&self) -> bool { [ "afs", "cifs", "coda", "ftpfs", "fuse.sshfs", "mfs", "ncpfs", "nfs", "nfs4", "smbfs", "sshfs", ] .contains(&self.mnt_type.as_str()) } pub fn named(name: String) -> Self { Self::new(name, "-".to_string(), "-".to_string(), "".to_string(), 0, 0) } const fn new( mnt_fsname: String, mnt_dir: String, mnt_type: String, mnt_opts: String, mnt_freq: i32, mnt_passno: i32, ) -> Self { Self { mnt_fsname, mnt_dir, mnt_type, mnt_opts, mnt_freq, mnt_passno, capacity: 0, free: 0, used: 0, statfs: None, } } } fn parse_mount_line(line: &str) -> Result { let mut mnt_a = line.split_whitespace(); Ok(Mount::new( mnt_a .next() .ok_or_else(|| anyhow!("Missing value fsname"))? .into(), mnt_a .next() .ok_or_else(|| anyhow!("Missing value dir"))? .into(), mnt_a .next() .ok_or_else(|| anyhow!("Missing value type"))? .into(), mnt_a .next() .ok_or_else(|| anyhow!("Missing value opts"))? .into(), mnt_a .next() .ok_or_else(|| anyhow!("Missing value freq"))? .parse::()?, mnt_a .next() .ok_or_else(|| anyhow!("Missing value passno"))? .parse::()?, )) } pub fn parse_mounts(f: File) -> Result> { BufReader::new(f) .lines() .map(|line| { parse_mount_line(&line?) .context("Failed to parse mount line") .map_err(Error::from) }) .collect::>>() } #[cfg(test)] mod tests { use super::*; #[test] fn parse_mounts() { let file = r#"sysfs /sys sysfs rw,nosuid,nodev,noexec,relatime 0 0 proc /proc proc rw,nosuid,nodev,noexec,relatime,hidepid=2 0 0 udev /dev devtmpfs rw,nosuid,relatime,size=2009144k,nr_inodes=502286,mode=755 0 0 devpts /dev/pts devpts rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=000 0 0 tmpfs /run tmpfs rw,nosuid,noexec,relatime,size=402800k,mode=755 0 0 /dev/mapper/vg0-root / ext4 rw,relatime,errors=remount-ro 0 0 tmpfs /run/lock tmpfs rw,nosuid,nodev,noexec,relatime,size=5120k 0 0 pstore /sys/fs/pstore pstore rw,relatime 0 0 configfs /sys/kernel/config configfs rw,relatime 0 0 tmpfs /run/shm tmpfs rw,nosuid,nodev,noexec,relatime,size=805580k 0 0 /dev/mapper/vg0-boot /boot ext4 rw,relatime 0 0 /dev/mapper/vg0-tmp /tmp ext4 rw,relatime 0 0 none /cgroup2 cgroup2 rw,relatime 0 0 "#; let mounts = file .lines() .map(|line| { parse_mount_line(&line) .context("Failed to parse mount line") .map_err(Error::from) }) .collect::>>() .unwrap(); assert_eq!(mounts.len(), 13); // nix::sys::statfs::Statfs doesn't have PartialEq let mnt = &mounts[0]; assert_eq!(mnt.mnt_fsname.as_str(), "sysfs"); assert_eq!(mnt.mnt_dir.as_str(), "/sys"); assert_eq!(mnt.mnt_type.as_str(), "sysfs"); assert_eq!(mnt.mnt_opts.as_str(), "rw,nosuid,nodev,noexec,relatime"); assert_eq!(mnt.mnt_freq, 0); assert_eq!(mnt.mnt_passno, 0); assert_eq!(mnt.capacity, 0); assert_eq!(mnt.free, 0); assert_eq!(mnt.used, 0); assert!(mnt.statfs.is_none()); } #[test] fn is_remote() { let mut mnt = Mount::named("foo".into()); mnt.mnt_type = String::from("nfs"); assert!(mnt.is_remote()); } #[test] fn is_local() { let mut mnt = Mount::named("foo".into()); mnt.mnt_type = String::from("btrfs"); assert!(mnt.is_local()); } } dfrs-0.0.7/src/theme.rs010064400017500000144000000044211401103444300130630ustar 00000000000000use crate::args::ColumnType; use colored::*; pub struct Theme { pub char_bar_filled: char, pub char_bar_empty: char, pub char_bar_open: String, pub char_bar_close: String, pub threshold_usage_medium: f32, pub threshold_usage_high: f32, pub color_heading: Option, pub color_usage_low: Option, pub color_usage_medium: Option, pub color_usage_high: Option, pub color_usage_void: Option, pub bar_width: usize, pub columns: Vec, } impl Theme { pub fn new() -> Self { Self { char_bar_filled: named_char::HEAVY_BOX, char_bar_empty: named_char::HEAVY_DOUBLE_DASH, char_bar_open: "".to_string(), char_bar_close: "".to_string(), threshold_usage_medium: 50.0, threshold_usage_high: 75.0, color_heading: Some(Color::Blue), color_usage_low: Some(Color::Green), color_usage_medium: Some(Color::Yellow), color_usage_high: Some(Color::Red), color_usage_void: Some(Color::Blue), bar_width: 20, columns: vec![ ColumnType::Filesystem, ColumnType::Type, ColumnType::Bar, ColumnType::UsedPercentage, ColumnType::Available, ColumnType::Used, ColumnType::Capacity, ColumnType::MountedOn, ], } } } #[allow(dead_code)] pub mod named_char { pub const SPACE: char = ' '; pub const EQUAL: char = '='; pub const HASHTAG: char = '#'; pub const ASTERISK: char = '*'; pub const LIGHT_BOX: char = '■'; pub const HEAVY_BOX: char = '▇'; pub const PERIOD: char = '.'; pub const DASH: char = '-'; pub const LONG_DASH: char = '—'; pub const LIGHT_HORIZONTAL: char = '─'; pub const HEAVY_HORIZONTAL: char = '━'; pub const LIGHT_DOUBLE_DASH: char = '╌'; pub const HEAVY_DOUBLE_DASH: char = '╍'; pub const ELLIPSIS: char = '…'; pub const SQUARE_BRACKET_OPEN: char = '['; pub const SQUARE_BRACKET_CLOSE: char = ']'; pub const LIGHT_VERTICAL: char = '│'; pub const LIGHT_VERTICAL_OPEN: char = '├'; pub const LIGHT_VERTICAL_CLOSE: char = '┤'; } dfrs-0.0.7/src/util.rs010064400017500000144000000257131401103444300127450ustar 00000000000000use crate::mount::Mount; use crate::theme::Theme; use colored::*; use std::cmp; use std::fmt; use std::io::{self, stdout, Write}; use std::path::Path; pub fn format_count(num: f64, delimiter: f64) -> String { let units = ["B", "k", "M", "G", "T", "P", "E", "Z", "Y"]; if num < 1_f64 { return format!("{}", num); } let exponent = cmp::min(num.log(delimiter).floor() as i32, (units.len() - 1) as i32); let pretty_bytes = format!("{:.*}", 1, num / delimiter.powi(exponent)); let unit = units[exponent as usize]; format!("{}{}", pretty_bytes, unit) } #[inline] pub fn format_percentage(percentage: Option) -> String { match percentage { Some(percentage) => format!( "{:>5.1}{}", (percentage * 10.0).round() / 10.0, "%".color(Color::White) ), None => format!("{:>6}", "-"), } } pub fn bar(width: usize, percentage: Option, theme: &Theme) -> String { let fill_len_total = (percentage.unwrap_or(0.0) as f32 / 100.0 * width as f32).ceil() as usize; let fill_len_low = std::cmp::min( fill_len_total, (width as f32 * theme.threshold_usage_medium / 100.0).ceil() as usize, ); let fill_len_medium = std::cmp::min( fill_len_total, (width as f32 * theme.threshold_usage_high / 100.0).ceil() as usize, ) - fill_len_low; let fill_len_high = fill_len_total - fill_len_low - fill_len_medium; let color_empty = match percentage { Some(_) => theme.color_usage_low, None => theme.color_usage_void, } .unwrap_or(Color::Green); let fill_low = theme .char_bar_filled .to_string() .repeat(fill_len_low) .color(theme.color_usage_low.unwrap_or(Color::Green)); let fill_medium = theme .char_bar_filled .to_string() .repeat(fill_len_medium) .color(theme.color_usage_medium.unwrap_or(Color::Yellow)); let fill_high = theme .char_bar_filled .to_string() .repeat(fill_len_high) .color(theme.color_usage_high.unwrap_or(Color::Red)); let empty = theme .char_bar_empty .to_string() .repeat(width - fill_len_total) .color(color_empty); format!( "{}{}{}{}{}{}", theme.char_bar_open, fill_low, fill_medium, fill_high, empty, theme.char_bar_close ) } pub fn lvm_alias(device: &str) -> Option { if !device.starts_with("/dev/mapper/") { return None; } let device = &device["/dev/mapper/".len()..].replace("--", "$$"); if !device.contains('-') { return None; } let mut it = device.splitn(2, '-'); let vg = it.next().unwrap_or(""); let lv = it.next().unwrap_or(""); Some(format!("/dev/{}/{}", vg, lv).replace("$$", "-")) } #[inline] pub fn get_best_mount_match<'a>(path: &Path, mnts: &'a [Mount]) -> Option<&'a Mount> { let scores = mnts .iter() .map(|mnt| (calculate_path_match_score(path, mnt), mnt)); let best = scores.max_by_key(|x| x.0)?; Some(best.1) } #[inline] pub fn calculate_path_match_score(path: &Path, mnt: &Mount) -> usize { if path.starts_with(&mnt.mnt_dir) { mnt.mnt_dir.len() } else { 0 } } #[inline] pub fn cmp_by_capacity_and_dir_name(a: &Mount, b: &Mount) -> cmp::Ordering { u64::min(1, a.capacity) .cmp(&u64::min(1, b.capacity)) .reverse() .then(a.mnt_dir.cmp(&b.mnt_dir)) } #[inline] pub fn mnt_matches_filter(mnt: &Mount, filter: &str) -> bool { if let Some(start) = filter.strip_suffix('*') { mnt.mnt_fsname.starts_with(start) } else { mnt.mnt_fsname == filter } } #[inline] pub fn calc_total(mnts: &[Mount]) -> Mount { let mut total = Mount::named("total".to_string()); total.free = mnts.iter().map(|mnt| mnt.free).sum(); total.used = mnts.iter().map(|mnt| mnt.used).sum(); total.capacity = mnts.iter().map(|mnt| mnt.capacity).sum(); total } #[inline] pub fn try_print(args: fmt::Arguments) -> io::Result<()> { stdout().write_fmt(args) } #[macro_export] macro_rules! try_print { ($($arg:tt)*) => ($crate::try_print(format_args!($($arg)*))); } #[macro_export] macro_rules! try_println { ($fmt:expr) => (try_print!(concat!($fmt, "\n"))); ($fmt:expr, $($arg:tt)*) => (try_print!(concat!($fmt, "\n"), $($arg)*)); } #[cfg(test)] mod tests { use super::*; use std::path::PathBuf; #[test] fn format_count_zero() { let s = format_count(0.0, 1024.0); assert_eq!(s, "0"); } #[test] fn format_count_bytes() { let s = format_count(12.0, 1024.0); assert_eq!(s, "12.0B"); } #[test] fn format_count_megabyte() { let s = format_count(12693000.0, 1024.0); assert_eq!(s, "12.1M"); } #[test] fn format_count_very_large() { let s = format_count(2535301200456458802993406410752.0, 1024.0); assert_eq!(s, "2097152.0Y"); } #[test] fn format_percentage_zero() { let s = format_percentage(Option::Some(0f32)); assert_eq!(s, format!(" 0.0{}", "%".color(Color::White))); } #[test] fn format_percentage_fraction() { let s = format_percentage(Option::Some(0.3333333333333333f32)); assert_eq!(s, format!(" 0.3{}", "%".color(Color::White))); } #[test] fn format_percentage_hundred() { let s = format_percentage(Option::Some(100f32)); assert_eq!(s, format!("100.0{}", "%".color(Color::White))); } #[test] fn format_percentage_none() { let s = format_percentage(Option::None); assert_eq!(s, " -"); } #[test] fn lvm_alias_none() { let s = lvm_alias("/dev/mapper/crypto"); assert_eq!(s, None); } #[test] fn lvm_alias_simple() { let s = lvm_alias("/dev/mapper/crypto-foo"); assert_eq!(s, Some("/dev/crypto/foo".to_string())); } #[test] fn lvm_alias_two_dashes() { let s = lvm_alias("/dev/mapper/crypto--foo"); assert_eq!(s, None); } #[test] fn lvm_alias_three_dashes() { let s = lvm_alias("/dev/mapper/crypto---foo"); assert_eq!(s, Some("/dev/crypto-/foo".to_string())); } #[test] fn lvm_alias_four_dashes() { let s = lvm_alias("/dev/mapper/crypto----foo"); assert_eq!(s, None); } #[test] fn lvm_alias_five_dashes() { let s = lvm_alias("/dev/mapper/crypto-----foo"); assert_eq!(s, Some("/dev/crypto--/foo".to_string())); } #[test] fn get_best_mount_match_simple() { let mut mnt1 = Mount::named("foo".into()); mnt1.mnt_dir = "/a".to_string(); let mut mnt2 = Mount::named("bar".into()); mnt2.mnt_dir = "/a/b".to_string(); let mut mnt3 = Mount::named("fizz".into()); mnt3.mnt_dir = "/a/b/c".to_string(); let mut mnt4 = Mount::named("buzz".into()); mnt4.mnt_dir = "/a/b/c/d".to_string(); let mnts = &[mnt1, mnt2, mnt3, mnt4]; let matched = get_best_mount_match(&PathBuf::from("/a/b/c"), mnts).unwrap(); assert_eq!(matched.mnt_dir, "/a/b/c"); } #[test] fn calculate_path_match_score_simple() { let mut mnt1 = Mount::named("foo".into()); mnt1.mnt_dir = "/a/s/d".to_string(); let score = calculate_path_match_score(&PathBuf::from("/a/s/d/f"), &mnt1); assert_eq!(score, 6); } #[test] fn cmp_by_capacity_and_dir_name_equal() { let mnt1 = Mount::named("foo".into()); let mnt2 = Mount::named("bar".into()); let ord = cmp_by_capacity_and_dir_name(&mnt1, &mnt2); assert_eq!(ord, cmp::Ordering::Equal); } #[test] fn cmp_by_capacity_and_dir_name_greater_capacity_greater_name() { let mut mnt1 = Mount::named("foo".into()); mnt1.capacity = 123; mnt1.mnt_dir = "/b".to_string(); let mut mnt2 = Mount::named("bar".into()); mnt2.capacity = 64; mnt2.mnt_dir = "/a".to_string(); let ord = cmp_by_capacity_and_dir_name(&mnt1, &mnt2); assert_eq!(ord, cmp::Ordering::Greater); } #[test] fn cmp_by_capacity_and_dir_name_smaller_capacity_greater_name() { let mut mnt1 = Mount::named("foo".into()); mnt1.capacity = 64; mnt1.mnt_dir = "/b".to_string(); let mut mnt2 = Mount::named("bar".into()); mnt2.capacity = 123; mnt2.mnt_dir = "/a".to_string(); let ord = cmp_by_capacity_and_dir_name(&mnt1, &mnt2); assert_eq!(ord, cmp::Ordering::Greater); } #[test] fn cmp_by_capacity_and_dir_name_greater_capacity_smaller_name() { let mut mnt1 = Mount::named("foo".into()); mnt1.capacity = 123; mnt1.mnt_dir = "/a".to_string(); let mut mnt2 = Mount::named("bar".into()); mnt2.capacity = 64; mnt2.mnt_dir = "/b".to_string(); let ord = cmp_by_capacity_and_dir_name(&mnt1, &mnt2); assert_eq!(ord, cmp::Ordering::Less); } #[test] fn cmp_by_capacity_and_dir_name_smaller_capacity_smaller_name() { let mut mnt1 = Mount::named("foo".into()); mnt1.capacity = 64; mnt1.mnt_dir = "/a".to_string(); let mut mnt2 = Mount::named("bar".into()); mnt2.capacity = 123; mnt2.mnt_dir = "/b".to_string(); let ord = cmp_by_capacity_and_dir_name(&mnt1, &mnt2); assert_eq!(ord, cmp::Ordering::Less); } #[test] fn cmp_by_capacity_and_dir_name_equal_capacity_greater_name() { let mut mnt1 = Mount::named("foo".into()); mnt1.capacity = 123; mnt1.mnt_dir = "/b".to_string(); let mut mnt2 = Mount::named("bar".into()); mnt2.capacity = 123; mnt2.mnt_dir = "/a".to_string(); let ord = cmp_by_capacity_and_dir_name(&mnt1, &mnt2); assert_eq!(ord, cmp::Ordering::Greater); } #[test] fn cmp_by_capacity_and_dir_name_greater_capacity_equal_name() { let mut mnt1 = Mount::named("foo".into()); mnt1.capacity = 123; mnt1.mnt_dir = "/a".to_string(); let mut mnt2 = Mount::named("bar".into()); mnt2.capacity = 64; mnt2.mnt_dir = "/a".to_string(); let ord = cmp_by_capacity_and_dir_name(&mnt1, &mnt2); assert_eq!(ord, cmp::Ordering::Equal); } #[test] fn calc_total_simple() { let mut mnt1 = Mount::named("foo".into()); mnt1.free = 123; mnt1.used = 456; mnt1.capacity = 123 + 456; let mut mnt2 = Mount::named("bar".into()); mnt2.free = 678; mnt2.used = 9123; mnt2.capacity = 678 + 9123; let mut mnt3 = Mount::named("fizz".into()); mnt3.free = 4567; mnt3.used = 0; mnt3.capacity = 4567; let mut mnt4 = Mount::named("buzz".into()); mnt4.free = 0; mnt4.used = 890123; mnt4.capacity = 890123; let total = calc_total(&[mnt1, mnt2, mnt3, mnt4]); assert_eq!(total.mnt_fsname, "total"); assert_eq!(total.free, 5368); assert_eq!(total.used, 899702); assert_eq!(total.capacity, 5368 + 899702); } }