Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
openSUSE:Factory:Rebuild
fuc
fuc-2.2.0.obscpio
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File fuc-2.2.0.obscpio of Package fuc
07070100000000000041ED0000000000000000000000026682237800000000000000000000000000000000000000000000001200000000fuc-2.2.0/.github07070100000001000081A40000000000000000000000016682237800000013000000000000000000000000000000000000001E00000000fuc-2.2.0/.github/FUNDING.ymlgithub: SUPERCILEX 07070100000002000041ED0000000000000000000000026682237800000000000000000000000000000000000000000000001C00000000fuc-2.2.0/.github/workflows07070100000003000081A40000000000000000000000016682237800001205000000000000000000000000000000000000002400000000fuc-2.2.0/.github/workflows/cid.ymlname: CI/CD on: [push, pull_request] jobs: build: strategy: matrix: os: [ubuntu-latest, macos-latest, windows-latest] runs-on: ${{ matrix.os }} steps: - name: Checkout uses: actions/checkout@v3 - name: Cargo Cache uses: actions/cache@v3 with: path: | ~/.cargo/bin/ ~/.cargo/registry/index/ ~/.cargo/registry/cache/ ~/.cargo/git/db/ target/ key: ${{ runner.os }}-cargo-build-${{ hashFiles('**/Cargo.lock') }} restore-keys: ${{ runner.os }}-cargo- - name: Build project run: cargo build --workspace --release test: strategy: matrix: os: [ubuntu-latest, macos-latest, windows-latest] runs-on: ${{ matrix.os }} steps: - name: Checkout uses: actions/checkout@v3 - name: Install Rust run: rustup component add rustfmt clippy - name: Cargo Cache uses: actions/cache@v3 with: path: | ~/.cargo/bin/ ~/.cargo/registry/index/ ~/.cargo/registry/cache/ ~/.cargo/git/db/ target/ key: ${{ runner.os }}-cargo-test-${{ hashFiles('**/Cargo.lock') }} restore-keys: ${{ runner.os }}-cargo- - name: Run tests run: cargo test --workspace deploy_release: needs: [build, test] runs-on: ubuntu-latest if: startsWith(github.ref, 'refs/tags/') steps: - name: Checkout uses: actions/checkout@v3 - name: Publish release run: | cargo publish --package fuc_engine cargo publish --package rmz cargo publish --package cpz env: CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} attach_binaries: strategy: fail-fast: false matrix: include: - target: x86_64-unknown-linux-gnu os: ubuntu-latest tool: cargo - target: x86_64-unknown-linux-musl os: ubuntu-latest tool: cargo - target: aarch64-unknown-linux-gnu os: ubuntu-latest tool: RUSTFLAGS="-Ctarget-feature=-outline-atomics" cross - target: riscv64gc-unknown-linux-gnu os: ubuntu-latest tool: cross - target: x86_64-apple-darwin os: macos-latest tool: cargo - target: aarch64-apple-darwin os: macos-latest tool: cross - target: x86_64-pc-windows-msvc os: windows-latest tool: cargo - target: aarch64-pc-windows-msvc os: windows-latest tool: cross needs: [build, test] runs-on: ${{ matrix.os }} if: startsWith(github.ref, 'refs/tags/') steps: - name: Checkout uses: actions/checkout@v3 - name: Install Rust run: | rustup target add ${{ matrix.target }} rustup component add rust-src - name: Install cross if: contains(matrix.tool, 'cross') run: cargo install cross - name: Build binary run: ${{ matrix.tool }} build --workspace --release --locked --target=${{ matrix.target }} -Z build-std=std,panic_abort -Z build-std-features=panic_immediate_abort - name: Upload binary if: matrix.os != 'windows-latest' uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} file: target/${{ matrix.target }}/release/rmz asset_name: rmz-${{ matrix.target }} tag: ${{ github.ref }} - name: Upload binary if: matrix.os != 'windows-latest' uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} file: target/${{ matrix.target }}/release/cpz asset_name: cpz-${{ matrix.target }} tag: ${{ github.ref }} - name: Upload binary if: matrix.os == 'windows-latest' uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} file: target/${{ matrix.target }}/release/rmz.exe asset_name: rmz-${{ matrix.target }}.exe tag: ${{ github.ref }} - name: Upload binary if: matrix.os == 'windows-latest' uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} file: target/${{ matrix.target }}/release/cpz.exe asset_name: cpz-${{ matrix.target }}.exe tag: ${{ github.ref }} 07070100000004000081A40000000000000000000000016682237800000008000000000000000000000000000000000000001500000000fuc-2.2.0/.gitignore/target 07070100000005000081A4000000000000000000000001668223780000E382000000000000000000000000000000000000001500000000fuc-2.2.0/Cargo.lock# This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "addr2line" version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" dependencies = [ "gimli", ] [[package]] name = "adler" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "aho-corasick" version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] [[package]] name = "aligned" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "377e4c0ba83e4431b10df45c1d4666f178ea9c552cac93e60c3a88bf32785923" dependencies = [ "as-slice", ] [[package]] name = "anes" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" [[package]] name = "anstream" version = "0.6.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", "is_terminal_polyfill", "utf8parse", ] [[package]] name = "anstyle" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" [[package]] name = "anstyle-parse" version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391" dependencies = [ "windows-sys 0.52.0", ] [[package]] name = "anstyle-wincon" version = "3.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" dependencies = [ "anstyle", "windows-sys 0.52.0", ] [[package]] name = "anyhow" version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" [[package]] name = "arrayvec" version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" [[package]] name = "as-slice" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "516b6b4f0e40d50dcda9365d53964ec74560ad4284da2e7fc97122cd83174516" dependencies = [ "stable_deref_trait", ] [[package]] name = "autocfg" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "automod" version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "edf3ee19dbc0a46d740f6f0926bde8c50f02bdbc7b536842da28f6ac56513a8b" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "backtrace" version = "0.3.73" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" dependencies = [ "addr2line", "cc", "cfg-if", "libc", "miniz_oxide", "object", "rustc-demangle", ] [[package]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "bumpalo" version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "bytesize" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3e368af43e418a04d52505cf3dbc23dda4e3407ae2fa99fd0e4f308ce546acc" [[package]] name = "cache-size" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df5c3aca3f278263eecfa3e6d2cd1fdd76029f68a0c9e9ed5ab55f0508e70723" dependencies = [ "raw-cpuid", ] [[package]] name = "camino" version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e0ec6b951b160caa93cc0c7b209e5a3bff7aae9062213451ac99493cd844c239" dependencies = [ "serde", ] [[package]] name = "cargo-manifest" version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7db7ad32d2729eca70d1669bae38b6a29dabc30a16f1892cc00977873f450d0a" dependencies = [ "serde", "thiserror", "toml", ] [[package]] name = "cargo-platform" version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24b1f0365a6c6bb4020cd05806fd0d33c44d38046b8bd7f0e40814b9763cabfc" dependencies = [ "serde", ] [[package]] name = "cargo_metadata" version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037" dependencies = [ "camino", "cargo-platform", "semver", "serde", "serde_json", "thiserror", ] [[package]] name = "cast" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2755ff20a1d93490d26ba33a6f092a38a508398a5320df5d4b3014fcccce9410" [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "ciborium" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" dependencies = [ "ciborium-io", "ciborium-ll", "serde", ] [[package]] name = "ciborium-io" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" [[package]] name = "ciborium-ll" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" dependencies = [ "ciborium-io", "half", ] [[package]] name = "clap" version = "4.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "84b3edb18336f4df585bc9aa31dd99c036dfa5dc5e9a2939a722a188f3a8970d" dependencies = [ "clap_builder", "clap_derive", ] [[package]] name = "clap-num" version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e063d263364859dc54fb064cedb7c122740cd4733644b14b176c097f51e8ab7" dependencies = [ "num-traits", ] [[package]] name = "clap-verbosity-flag2" version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e7010555bed836b2d8b61853d4c7377a86585b972786dd5c445881916cdb4bb" dependencies = [ "clap", "log", ] [[package]] name = "clap_builder" version = "4.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1c09dd5ada6c6c78075d6fd0da3f90d8080651e2d6cc8eb2f1aaa4034ced708" dependencies = [ "anstream", "anstyle", "clap_lex", "strsim", "terminal_size", ] [[package]] name = "clap_derive" version = "4.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2bac35c6dafb060fd4d275d9a4ffae97917c13a6327903a8be2153cd964f7085" dependencies = [ "heck", "proc-macro2", "quote", "syn", ] [[package]] name = "clap_lex" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70" [[package]] name = "colorchoice" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" [[package]] name = "console" version = "0.15.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb" dependencies = [ "encode_unicode", "lazy_static", "libc", "unicode-width", "windows-sys 0.52.0", ] [[package]] name = "content_inspector" version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7bda66e858c683005a53a9a60c69a4aca7eeaa45d124526e389f7aec8e62f38" dependencies = [ "memchr", ] [[package]] name = "cp_rayon" version = "0.0.0" dependencies = [ "rayon", ] [[package]] name = "cp_stdlib" version = "0.0.0" [[package]] name = "cpz" version = "2.2.0" dependencies = [ "cache-size", "clap", "criterion", "error-stack", "fuc_engine", "indicatif", "memmap2", "once_cell", "rand", "rustix", "supercilex-tests", "tempfile", "thiserror", "tracing", "tracing-indicatif", "tracing-subscriber", "tracing-tracy", "tracy-client", "trycmd", ] [[package]] name = "criterion" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f" dependencies = [ "anes", "cast", "ciborium", "clap", "criterion-plot", "is-terminal", "itertools", "num-traits", "once_cell", "oorandom", "plotters", "rayon", "regex", "serde", "serde_derive", "serde_json", "tinytemplate", "walkdir", ] [[package]] name = "criterion-plot" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" dependencies = [ "cast", "itertools", ] [[package]] name = "crossbeam-channel" version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-deque" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" dependencies = [ "crossbeam-epoch", "crossbeam-utils", ] [[package]] name = "crossbeam-epoch" version = "0.9.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-utils" version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" [[package]] name = "crunchy" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" [[package]] name = "cvt" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2ae9bf77fbf2d39ef573205d554d87e86c12f1994e9ea335b0651b9b278bcf1" dependencies = [ "cfg-if", ] [[package]] name = "dissimilar" version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59f8e79d1fbf76bdfbde321e902714bf6c49df88a7dda6fc682fc2979226962d" [[package]] name = "dunce" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" [[package]] name = "either" version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] name = "encode_unicode" version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" [[package]] name = "env_filter" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a009aa4810eb158359dda09d0c87378e4bbb89b5a801f016885a4707ba24f7ea" dependencies = [ "log", ] [[package]] name = "env_logger" version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38b35839ba51819680ba087cd351788c9a3c476841207e0b8cee0b04722343b9" dependencies = [ "anstream", "anstyle", "env_filter", "log", ] [[package]] name = "equivalent" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" dependencies = [ "libc", "windows-sys 0.52.0", ] [[package]] name = "error-stack" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "27a72baa257b5e0e2de241967bc5ee8f855d6072351042688621081d66b2a76b" dependencies = [ "anyhow", "rustc_version", ] [[package]] name = "expect-test" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e0be0a561335815e06dab7c62e50353134c796e7a6155402a64bcff66b6a5e0" dependencies = [ "dissimilar", "once_cell", ] [[package]] name = "fastrand" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" [[package]] name = "filetime" version = "0.2.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd" dependencies = [ "cfg-if", "libc", "redox_syscall", "windows-sys 0.52.0", ] [[package]] name = "fs_at" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "982f82cc75107eef84f417ad6c53ae89bf65b561937ca4a3b3b0fd04d0aa2425" dependencies = [ "aligned", "cfg-if", "cvt", "libc", "nix", "windows-sys 0.48.0", ] [[package]] name = "ftzz" version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5eb6877e007a463d07b1f803e825adeaef41036b6da4f1c82d1d94d407561239" dependencies = [ "bytesize", "cfg-if", "clap", "clap-num", "clap-verbosity-flag2", "env_logger", "error-stack", "io-adapters", "itoa", "log", "rand", "rand_distr", "rand_xoshiro", "rustix", "sysexits", "thiserror", "thousands", "tokio", "typed-builder", ] [[package]] name = "fuc_engine" version = "2.2.0" dependencies = [ "crossbeam-channel", "ftzz", "io-adapters", "once_cell", "rayon", "remove_dir_all", "rstest", "rustix", "supercilex-tests", "tempfile", "thiserror", "tracing", "typed-builder", ] [[package]] name = "futures" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" dependencies = [ "futures-channel", "futures-core", "futures-executor", "futures-io", "futures-sink", "futures-task", "futures-util", ] [[package]] name = "futures-channel" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" dependencies = [ "futures-core", "futures-sink", ] [[package]] name = "futures-core" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" [[package]] name = "futures-executor" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" dependencies = [ "futures-core", "futures-task", "futures-util", ] [[package]] name = "futures-io" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" [[package]] name = "futures-macro" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "futures-sink" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" [[package]] name = "futures-task" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" [[package]] name = "futures-util" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" dependencies = [ "futures-channel", "futures-core", "futures-io", "futures-macro", "futures-sink", "futures-task", "memchr", "pin-project-lite", "pin-utils", "slab", ] [[package]] name = "generator" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "186014d53bc231d0090ef8d6f03e0920c54d85a5ed22f4f2f74315ec56cf83fb" dependencies = [ "cc", "cfg-if", "libc", "log", "rustversion", "windows", ] [[package]] name = "getrandom" version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", "wasi", ] [[package]] name = "gimli" version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" [[package]] name = "glob" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "half" version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" dependencies = [ "cfg-if", "crunchy", ] [[package]] name = "hashbag" version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98f494b2060b2a8f5e63379e1e487258e014cee1b1725a735816c0107a2e9d93" [[package]] name = "hashbrown" version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" [[package]] name = "heck" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hermit-abi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" [[package]] name = "humantime" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "humantime-serde" version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57a3db5ea5923d99402c94e9feb261dc5ee9b4efa158b0315f788cf549cc200c" dependencies = [ "humantime", "serde", ] [[package]] name = "indexmap" version = "2.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" dependencies = [ "equivalent", "hashbrown", ] [[package]] name = "indicatif" version = "0.17.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "763a5a8f45087d6bcea4222e7b72c291a054edf80e4ef6efd2a4979878c7bea3" dependencies = [ "console", "instant", "number_prefix", "portable-atomic", "unicode-width", "vt100", ] [[package]] name = "instant" version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" dependencies = [ "cfg-if", ] [[package]] name = "io-adapters" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fa801fc0447544dc703e039e880b50296901e20c235337d08bb5e55eec6e500" [[package]] name = "is-terminal" version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" dependencies = [ "hermit-abi", "libc", "windows-sys 0.52.0", ] [[package]] name = "is_terminal_polyfill" version = "1.70.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" [[package]] name = "itertools" version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" dependencies = [ "either", ] [[package]] name = "itoa" version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "js-sys" version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" dependencies = [ "wasm-bindgen", ] [[package]] name = "lazy_static" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] name = "libm" version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" [[package]] name = "lint" version = "0.0.0" dependencies = [ "supercilex-tests", ] [[package]] name = "linux-raw-sys" version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" [[package]] name = "log" version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "loom" version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "419e0dc8046cb947daa77eb95ae174acfbddb7673b4151f56d1eed8e93fbfaca" dependencies = [ "cfg-if", "generator", "scoped-tls", "tracing", "tracing-subscriber", ] [[package]] name = "matchers" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" dependencies = [ "regex-automata 0.1.10", ] [[package]] name = "memchr" version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "memmap2" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe751422e4a8caa417e13c3ea66452215d7d63e19e604f4980461212f3ae1322" dependencies = [ "libc", ] [[package]] name = "miniz_oxide" version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" dependencies = [ "adler", ] [[package]] name = "nix" version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" dependencies = [ "bitflags 1.3.2", "cfg-if", "libc", ] [[package]] name = "normalize-line-endings" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" [[package]] name = "normpath" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5831952a9476f2fed74b77d74182fa5ddc4d21c72ec45a333b250e3ed0272804" dependencies = [ "windows-sys 0.52.0", ] [[package]] name = "nu-ansi-term" version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" dependencies = [ "overload", "winapi", ] [[package]] name = "num-traits" version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", "libm", ] [[package]] name = "number_prefix" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" [[package]] name = "object" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "081b846d1d56ddfc18fdf1a922e4f6e07a11768ea1b92dec44e42b72712ccfce" dependencies = [ "memchr", ] [[package]] name = "once_cell" version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "oorandom" version = "11.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" [[package]] name = "os_pipe" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29d73ba8daf8fac13b0501d1abeddcfe21ba7401ada61a819144b6c2a4f32209" dependencies = [ "libc", "windows-sys 0.52.0", ] [[package]] name = "overload" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" [[package]] name = "pin-project-lite" version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" [[package]] name = "pin-utils" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "plotters" version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a15b6eccb8484002195a3e44fe65a4ce8e93a625797a063735536fd59cb01cf3" dependencies = [ "num-traits", "plotters-backend", "plotters-svg", "wasm-bindgen", "web-sys", ] [[package]] name = "plotters-backend" version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "414cec62c6634ae900ea1c56128dfe87cf63e7caece0852ec76aba307cebadb7" [[package]] name = "plotters-svg" version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81b30686a7d9c3e010b84284bdd26a29f2138574f52f5eb6f794fc0ad924e705" dependencies = [ "plotters-backend", ] [[package]] name = "portable-atomic" version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0" [[package]] name = "ppv-lite86" version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro2" version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ "unicode-ident", ] [[package]] name = "public-api" version = "0.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7003a241dfaf906198691b8c8237be738392a8f54249ccdd207bb56f7304156e" dependencies = [ "hashbag", "rustdoc-types", "serde", "serde_json", "thiserror", ] [[package]] name = "quote" version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ "proc-macro2", ] [[package]] name = "rand" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", "rand_chacha", "rand_core", ] [[package]] name = "rand_chacha" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", "rand_core", ] [[package]] name = "rand_core" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ "getrandom", ] [[package]] name = "rand_distr" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32cb0b9bc82b0a0876c2dd994a7e7a2683d3e7390ca40e6886785ef0c7e3ee31" dependencies = [ "num-traits", "rand", ] [[package]] name = "rand_xoshiro" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f97cdb2a36ed4183de61b2f824cc45c9f1037f28afe0a322e9fff4c108b5aaa" dependencies = [ "rand_core", ] [[package]] name = "raw-cpuid" version = "11.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e29830cbb1290e404f24c73af91c5d8d631ce7e128691e9477556b540cd01ecd" dependencies = [ "bitflags 2.6.0", ] [[package]] name = "rayon" version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" dependencies = [ "either", "rayon-core", ] [[package]] name = "rayon-core" version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" dependencies = [ "crossbeam-deque", "crossbeam-utils", ] [[package]] name = "redox_syscall" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" dependencies = [ "bitflags 1.3.2", ] [[package]] name = "regex" version = "1.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" dependencies = [ "aho-corasick", "memchr", "regex-automata 0.4.7", "regex-syntax 0.8.4", ] [[package]] name = "regex-automata" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" dependencies = [ "regex-syntax 0.6.29", ] [[package]] name = "regex-automata" version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" dependencies = [ "aho-corasick", "memchr", "regex-syntax 0.8.4", ] [[package]] name = "regex-syntax" version = "0.6.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" [[package]] name = "relative-path" version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2" [[package]] name = "remove_dir_all" version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23895cfadc1917fed9c6ed76a8c2903615fa3704f7493ff82b364c6540acc02b" dependencies = [ "aligned", "cfg-if", "cvt", "fs_at", "lazy_static", "libc", "normpath", "rayon", "windows-sys 0.45.0", ] [[package]] name = "rm_og_crappy" version = "0.0.0" dependencies = [ "futures", "tokio", ] [[package]] name = "rm_rayon" version = "0.0.0" dependencies = [ "rayon", ] [[package]] name = "rm_remove_dir_all" version = "0.0.0" dependencies = [ "remove_dir_all", ] [[package]] name = "rm_stdlib" version = "0.0.0" [[package]] name = "rmz" version = "2.2.0" dependencies = [ "clap", "criterion", "error-stack", "ftzz", "fuc_engine", "indicatif", "io-adapters", "remove_dir_all", "rm_og_crappy", "rm_rayon", "supercilex-tests", "tempfile", "thiserror", "tracing", "tracing-indicatif", "tracing-subscriber", "tracing-tracy", "tracy-client", "trycmd", ] [[package]] name = "rstest" version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9afd55a67069d6e434a95161415f5beeada95a01c7b815508a82dcb0e1593682" dependencies = [ "rstest_macros", "rustc_version", ] [[package]] name = "rstest_macros" version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4165dfae59a39dd41d8dec720d3cbfbc71f69744efb480a3920f5d4e0cc6798d" dependencies = [ "cfg-if", "glob", "proc-macro2", "quote", "regex", "relative-path", "rustc_version", "syn", "unicode-ident", ] [[package]] name = "rustc-demangle" version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustc_version" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ "semver", ] [[package]] name = "rustdoc-json" version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02afaf2615d8562f76c2a98b61c3d577304dc6429ea8ebaf654b3249e9197ac3" dependencies = [ "cargo-manifest", "cargo_metadata", "serde", "thiserror", "toml", "tracing", ] [[package]] name = "rustdoc-types" version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b9be1bc4a0ec3445cfa2e4ba112827544890d43d68b7d1eda5359a9c09d2cd8" dependencies = [ "serde", ] [[package]] name = "rustix" version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ "bitflags 2.6.0", "errno", "libc", "linux-raw-sys", "windows-sys 0.52.0", ] [[package]] name = "rustversion" version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" [[package]] name = "ryu" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "same-file" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" dependencies = [ "winapi-util", ] [[package]] name = "scoped-tls" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" [[package]] name = "semver" version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" dependencies = [ "serde", ] [[package]] name = "serde" version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "serde_json" version = "1.0.119" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8eddb61f0697cc3989c5d64b452f5488e2b8a60fd7d5076a3045076ffef8cb0" dependencies = [ "itoa", "ryu", "serde", ] [[package]] name = "serde_spanned" version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0" dependencies = [ "serde", ] [[package]] name = "sharded-slab" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" dependencies = [ "lazy_static", ] [[package]] name = "shlex" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "similar" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa42c91313f1d05da9b26f267f931cf178d4aba455b4c4622dd7355eb80c6640" [[package]] name = "slab" version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" dependencies = [ "autocfg", ] [[package]] name = "smallvec" version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "snapbox" version = "0.6.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40e14d10e4c2b4331ac24c33baa5a03e1fbca81c045b285b53b2a612d28569fb" dependencies = [ "anstream", "anstyle", "content_inspector", "dunce", "filetime", "libc", "normalize-line-endings", "os_pipe", "similar", "snapbox-macros", "tempfile", "wait-timeout", "walkdir", "windows-sys 0.52.0", ] [[package]] name = "snapbox-macros" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1f4c14672714436c09254801c934b203196a51182a5107fb76591c7cc56424d" dependencies = [ "anstream", ] [[package]] name = "stable_deref_trait" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] name = "strsim" version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "supercilex-tests" version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "823c777b550db7aea7f73855904f88b78c8b88071376852f80b057840e6ce053" dependencies = [ "clap_builder", "expect-test", "public-api", "rustdoc-json", ] [[package]] name = "syn" version = "2.0.68" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "901fa70d88b9d6c98022e23b4136f9f3e54e4662c3bc1bd1d84a42a9a0f0c1e9" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "sysexits" version = "0.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5b86c78a4dc8f92a6c34c96eea37290389c40fe3ffe8a4dd9898cf8a1f14282" [[package]] name = "tempfile" version = "3.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" dependencies = [ "cfg-if", "fastrand", "rustix", "windows-sys 0.52.0", ] [[package]] name = "terminal_size" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21bebf2b7c9e0a515f6e0f8c51dc0f8e4696391e6f1ff30379559f8365fb0df7" dependencies = [ "rustix", "windows-sys 0.48.0", ] [[package]] name = "thiserror" version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "thousands" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3bf63baf9f5039dadc247375c29eb13706706cfde997d0330d05aa63a77d8820" [[package]] name = "thread_local" version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" dependencies = [ "cfg-if", "once_cell", ] [[package]] name = "tinytemplate" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" dependencies = [ "serde", "serde_json", ] [[package]] name = "tokio" version = "1.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a" dependencies = [ "backtrace", "pin-project-lite", ] [[package]] name = "toml" version = "0.8.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f49eb2ab21d2f26bd6db7bf383edc527a7ebaee412d17af4d40fdccd442f335" dependencies = [ "indexmap", "serde", "serde_spanned", "toml_datetime", "toml_edit", ] [[package]] name = "toml_datetime" version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" dependencies = [ "serde", ] [[package]] name = "toml_edit" version = "0.22.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f21c7aaf97f1bd9ca9d4f9e73b0a6c74bd5afef56f2bc931943a6e1c37e04e38" dependencies = [ "indexmap", "serde", "serde_spanned", "toml_datetime", "winnow", ] [[package]] name = "tools" version = "0.0.0" [[package]] name = "tracing" version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ "pin-project-lite", "tracing-attributes", "tracing-core", ] [[package]] name = "tracing-attributes" version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "tracing-core" version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ "once_cell", "valuable", ] [[package]] name = "tracing-indicatif" version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "069580424efe11d97c3fef4197fa98c004fa26672cc71ad8770d224e23b1951d" dependencies = [ "indicatif", "tracing", "tracing-core", "tracing-subscriber", ] [[package]] name = "tracing-log" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" dependencies = [ "log", "once_cell", "tracing-core", ] [[package]] name = "tracing-subscriber" version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" dependencies = [ "matchers", "nu-ansi-term", "once_cell", "regex", "sharded-slab", "smallvec", "thread_local", "tracing", "tracing-core", "tracing-log", ] [[package]] name = "tracing-tracy" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6024d04f84a69fd0d1dc1eee3a2b070bd246530a0582f9982ae487cb6c703614" dependencies = [ "tracing-core", "tracing-subscriber", "tracy-client", ] [[package]] name = "tracy-client" version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59fb931a64ff88984f86d3e9bcd1ae8843aa7fe44dd0f8097527bc172351741d" dependencies = [ "loom", "once_cell", "tracy-client-sys", ] [[package]] name = "tracy-client-sys" version = "0.22.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d104d610dfa9dd154535102cc9c6164ae1fa37842bc2d9e83f9ac82b0ae0882" dependencies = [ "cc", ] [[package]] name = "trycmd" version = "0.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59709bd8eccada6a3fded26d22a7f2dcee406c18d3bd7ad2605ca3eeb8f6f6ec" dependencies = [ "automod", "glob", "humantime", "humantime-serde", "rayon", "serde", "shlex", "snapbox", "toml_edit", ] [[package]] name = "typed-builder" version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77739c880e00693faef3d65ea3aad725f196da38b22fdc7ea6ded6e1ce4d3add" dependencies = [ "typed-builder-macro", ] [[package]] name = "typed-builder-macro" version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f718dfaf347dcb5b983bfc87608144b0bad87970aebcbea5ce44d2a30c08e63" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-width" version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" [[package]] name = "utf8parse" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "valuable" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" [[package]] name = "vt100" version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "84cd863bf0db7e392ba3bd04994be3473491b31e66340672af5d11943c6274de" dependencies = [ "itoa", "log", "unicode-width", "vte", ] [[package]] name = "vte" version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f5022b5fbf9407086c180e9557be968742d839e68346af7792b8592489732197" dependencies = [ "arrayvec", "utf8parse", "vte_generate_state_changes", ] [[package]] name = "vte_generate_state_changes" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2e369bee1b05d510a7b4ed645f5faa90619e05437111783ea5848f28d97d3c2e" dependencies = [ "proc-macro2", "quote", ] [[package]] name = "wait-timeout" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" dependencies = [ "libc", ] [[package]] name = "walkdir" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" dependencies = [ "same-file", "winapi-util", ] [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" dependencies = [ "cfg-if", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", "syn", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" dependencies = [ "quote", "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", "syn", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" [[package]] name = "web-sys" version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" dependencies = [ "js-sys", "wasm-bindgen", ] [[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.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" dependencies = [ "windows-sys 0.52.0", ] [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows" version = "0.54.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9252e5725dbed82865af151df558e754e4a3c2c30818359eb17465f1346a1b49" dependencies = [ "windows-core", "windows-targets 0.52.5", ] [[package]] name = "windows-core" version = "0.54.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "12661b9c89351d684a50a8a643ce5f608e20243b9fb84687800163429f161d65" dependencies = [ "windows-result", "windows-targets 0.52.5", ] [[package]] name = "windows-result" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8" dependencies = [ "windows-targets 0.52.5", ] [[package]] name = "windows-sys" version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" dependencies = [ "windows-targets 0.42.2", ] [[package]] name = "windows-sys" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ "windows-targets 0.48.5", ] [[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ "windows-targets 0.52.5", ] [[package]] name = "windows-targets" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" dependencies = [ "windows_aarch64_gnullvm 0.42.2", "windows_aarch64_msvc 0.42.2", "windows_i686_gnu 0.42.2", "windows_i686_msvc 0.42.2", "windows_x86_64_gnu 0.42.2", "windows_x86_64_gnullvm 0.42.2", "windows_x86_64_msvc 0.42.2", ] [[package]] name = "windows-targets" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ "windows_aarch64_gnullvm 0.48.5", "windows_aarch64_msvc 0.48.5", "windows_i686_gnu 0.48.5", "windows_i686_msvc 0.48.5", "windows_x86_64_gnu 0.48.5", "windows_x86_64_gnullvm 0.48.5", "windows_x86_64_msvc 0.48.5", ] [[package]] name = "windows-targets" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" dependencies = [ "windows_aarch64_gnullvm 0.52.5", "windows_aarch64_msvc 0.52.5", "windows_i686_gnu 0.52.5", "windows_i686_gnullvm", "windows_i686_msvc 0.52.5", "windows_x86_64_gnu 0.52.5", "windows_x86_64_gnullvm 0.52.5", "windows_x86_64_msvc 0.52.5", ] [[package]] name = "windows_aarch64_gnullvm" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" [[package]] name = "windows_aarch64_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" [[package]] name = "windows_aarch64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" [[package]] name = "windows_i686_gnu" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" [[package]] name = "windows_i686_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" [[package]] name = "windows_i686_gnullvm" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" [[package]] name = "windows_i686_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" [[package]] name = "windows_i686_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" [[package]] name = "windows_x86_64_gnu" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" [[package]] name = "windows_x86_64_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" [[package]] name = "windows_x86_64_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" [[package]] name = "windows_x86_64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" [[package]] name = "winnow" version = "0.6.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59b5e5f6c299a3c7890b876a2a587f3115162487e704907d9b6cd29473052ba1" dependencies = [ "memchr", ] 07070100000006000081A4000000000000000000000001668223780000032D000000000000000000000000000000000000001500000000fuc-2.2.0/Cargo.toml[workspace] members = [ "cpz", "fuc_engine", "rmz", "comparisons/cp_rayon", "comparisons/cp_stdlib", "comparisons/rm_og_crappy", "comparisons/rm_rayon", "comparisons/rm_remove_dir_all", "comparisons/rm_stdlib", "comparisons/tools", ] [workspace.package] version = "2.2.0" authors = ["Alex Saveau <saveau.alexandre@gmail.com>"] edition = "2021" repository = "https://github.com/SUPERCILEX/fuc" license = "Apache-2.0" rust-version = "1.79" [package] name = "lint" version = "0.0.0" edition.workspace = true publish = false [dev-dependencies] supercilex-tests = { version = "0.4.6", default-features = false } [profile.release] lto = true codegen-units = 1 strip = true panic = "abort" [profile.dr] inherits = "release" debug = true debug-assertions = true strip = false 07070100000007000081A40000000000000000000000016682237800002C5E000000000000000000000000000000000000001200000000fuc-2.2.0/LICENSE Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. 07070100000008000081A40000000000000000000000016682237800000530000000000000000000000000000000000000001400000000fuc-2.2.0/README.md# Fast Unix Commands [![cpz crates.io](https://img.shields.io/crates/v/cpz?label=cpz%20crates.io)](https://crates.io/crates/cpz) [![rmz crates.io](https://img.shields.io/crates/v/rmz?label=rmz%20crates.io)](https://crates.io/crates/rmz) [![Packaging status](https://repology.org/badge/tiny-repos/fuc.svg)](https://repology.org/project/fuc/badges) The FUC-ing project provides modern unix commands focused on performance: - [`cpz`](cpz): a zippy alternative to [`cp`](https://man7.org/linux/man-pages/man1/cp.1.html) - [`rmz`](rmz): a zippy alternative to [`rm`](https://man7.org/linux/man-pages/man1/rm.1.html) Benchmarks are available under the [`comparisons`](comparisons) folder. ## Goals 1. Performance: if a reasonable improvement can be made, it will be. 2. Efficiency: when only negligible performance improvements are left, remaining efforts are focussed on minimizing wasted compute. 3. Usability: where applicable, the UX of existing commands is improved. ## Non-goals - Portability: FUCs are primarily targeted at modern Linux installations. Support for other platforms is provided on a best-efforts basis. - Compatibility: [coreutils](https://github.com/coreutils/coreutils) or its [Rust re-implementation](https://github.com/uutils/coreutils) will have the broadest and most stable set of options. 07070100000009000041ED0000000000000000000000026682237800000000000000000000000000000000000000000000001600000000fuc-2.2.0/comparisons0707010000000A000081A4000000000000000000000001668223780000A64F000000000000000000000000000000000000002000000000fuc-2.2.0/comparisons/README.md# Benchmarks The benchmarks take several hours to run and create hundreds of millions of files. Run at your own risk. :) ## Setup Run all commands in the root of this repository. ```bash cargo install hyperfine ftzz cargo b --workspace --release mkdir benches /tmp/empty ``` ### My environment - Linux: 5.14.0-162.18.1.el9_1.x86_64 - CPU: Intel i7-7700 - Memory: 15GiB - `/dev/sda6 /tmp xfs rw,nosuid,nodev,relatime,attr2,inode64,logbufs=8,logbsize=32k,noquota 0 0` > The macOS/Windows implementations are currently equivalent to the `*_rayon` implementations shown > in the benchmarks. ## Remove ### Run ```bash for num_bytes in {0,1G}; do for num_files in {10,10_000,100_000,1M}; do hyperfine --warmup 3 -N \ --export-markdown "benches/remove_${num_files}_files_${num_bytes}_bytes.md" \ --export-json "benches/remove_${num_files}_files_${num_bytes}_bytes.json" \ --prepare "ftzz g -n ${num_files} -b ${num_bytes} /tmp/ftzz" \ "rm -r /tmp/ftzz" \ "find /tmp/ftzz -delete" \ "rsync --delete -r /tmp/empty/ /tmp/ftzz" \ "./target/release/rm_stdlib /tmp/ftzz" \ "./target/release/rm_rayon /tmp/ftzz" \ "./target/release/rm_remove_dir_all /tmp/ftzz" \ "./target/release/rmz /tmp/ftzz" done hyperfine --warmup 3 -N \ --export-markdown "benches/remove_100_000_files_${num_bytes}_bytes_0_depth.md" \ --export-json "benches/remove_100_000_files_${num_bytes}_bytes_0_depth.json" \ --prepare "ftzz g -n 100_000 -b ${num_bytes} -d 0 /tmp/ftzz" \ "rm -r /tmp/ftzz" \ "find /tmp/ftzz -delete" \ "rsync --delete -r /tmp/empty/ /tmp/ftzz" \ "perl -e 'for(</tmp/ftzz/*>){unlink}'" \ "./target/release/rm_stdlib /tmp/ftzz" \ "./target/release/rm_rayon /tmp/ftzz" \ "./target/release/rm_remove_dir_all /tmp/ftzz" \ "./target/release/rmz /tmp/ftzz" hyperfine --warmup 3 -N \ --export-markdown "benches/remove_100_000_files_${num_bytes}_bytes_5_files_per_dir.md" \ --export-json "benches/remove_100_000_files_${num_bytes}_bytes_5_files_per_dir.json" \ --prepare "ftzz g -n 100_000 -b ${num_bytes} -r 5 /tmp/ftzz" \ "rm -r /tmp/ftzz" \ "find /tmp/ftzz -delete" \ "rsync --delete -r /tmp/empty/ /tmp/ftzz" \ "./target/release/rm_stdlib /tmp/ftzz" \ "./target/release/rm_rayon /tmp/ftzz" \ "./target/release/rm_remove_dir_all /tmp/ftzz" \ "./target/release/rmz /tmp/ftzz" done ``` ### Results #### `remove_1M_files_0_bytes.md` | Command | Mean [s] | Min [s] | Max [s] | User [s] | System [s] | Relative | |:-----------------------------------------------|---------------:|--------:|--------:|---------:|-----------:|------------:| | *`./target/release/rmz /tmp/ftzz`* | 4.711 ± 0.649 | 4.208 | 6.099 | 0.535 | 18.650 | 1.00 | | `./target/release/rm_rayon /tmp/ftzz` | 5.689 ± 0.549 | 5.007 | 6.630 | 1.253 | 20.088 | 1.21 ± 0.20 | | `./target/release/rm_stdlib /tmp/ftzz` | 9.345 ± 0.063 | 9.282 | 9.457 | 0.377 | 8.708 | 1.98 ± 0.27 | | `rm -r /tmp/ftzz` | 9.491 ± 0.059 | 9.444 | 9.650 | 0.465 | 8.768 | 2.01 ± 0.28 | | `find /tmp/ftzz -delete` | 9.535 ± 0.051 | 9.434 | 9.620 | 0.485 | 8.804 | 2.02 ± 0.28 | | `rsync --delete -r /tmp/empty/ /tmp/ftzz` | 11.046 ± 0.049 | 10.989 | 11.120 | 0.911 | 9.914 | 2.34 ± 0.32 | | `./target/release/rm_remove_dir_all /tmp/ftzz` | 12.647 ± 0.088 | 12.532 | 12.758 | 1.376 | 10.950 | 2.68 ± 0.37 | #### `remove_1M_files_1G_bytes.md` | Command | Mean [s] | Min [s] | Max [s] | User [s] | System [s] | Relative | |:-----------------------------------------------|---------------:|--------:|--------:|---------:|-----------:|------------:| | *`./target/release/rmz /tmp/ftzz`* | 6.559 ± 0.463 | 5.977 | 7.689 | 0.556 | 28.872 | 1.00 | | `./target/release/rm_rayon /tmp/ftzz` | 7.765 ± 0.454 | 7.047 | 8.567 | 1.331 | 30.759 | 1.18 ± 0.11 | | `rsync --delete -r /tmp/empty/ /tmp/ftzz` | 18.924 ± 0.321 | 18.394 | 19.428 | 0.977 | 16.014 | 2.89 ± 0.21 | | `./target/release/rm_stdlib /tmp/ftzz` | 22.892 ± 0.885 | 21.842 | 24.252 | 0.451 | 17.758 | 3.49 ± 0.28 | | `find /tmp/ftzz -delete` | 23.020 ± 0.755 | 21.342 | 23.911 | 0.601 | 17.805 | 3.51 ± 0.27 | | `rm -r /tmp/ftzz` | 23.354 ± 0.990 | 21.777 | 24.772 | 0.563 | 17.907 | 3.56 ± 0.29 | | `./target/release/rm_remove_dir_all /tmp/ftzz` | 26.357 ± 0.758 | 24.981 | 27.640 | 1.505 | 20.445 | 4.02 ± 0.31 | #### `remove_100_000_files_0_bytes.md` | Command | Mean [ms] | Min [ms] | Max [ms] | User [ms] | System [ms] | Relative | |:-----------------------------------------------|--------------:|---------:|---------:|----------:|------------:|------------:| | *`./target/release/rmz /tmp/ftzz`* | 459.3 ± 96.0 | 369.7 | 583.3 | 65.2 | 1697.5 | 1.00 | | `./target/release/rm_rayon /tmp/ftzz` | 648.1 ± 94.7 | 535.5 | 811.3 | 158.6 | 2121.1 | 1.41 ± 0.36 | | `./target/release/rm_stdlib /tmp/ftzz` | 804.2 ± 19.5 | 777.9 | 848.2 | 42.5 | 723.3 | 1.75 ± 0.37 | | `find /tmp/ftzz -delete` | 826.2 ± 22.6 | 796.3 | 853.0 | 55.3 | 728.1 | 1.80 ± 0.38 | | `rm -r /tmp/ftzz` | 873.7 ± 54.2 | 813.7 | 968.5 | 53.2 | 744.3 | 1.90 ± 0.41 | | `rsync --delete -r /tmp/empty/ /tmp/ftzz` | 1012.7 ± 16.8 | 996.3 | 1045.1 | 96.1 | 846.1 | 2.21 ± 0.46 | | `./target/release/rm_remove_dir_all /tmp/ftzz` | 1132.0 ± 46.9 | 1099.2 | 1243.2 | 136.3 | 945.9 | 2.46 ± 0.53 | #### `remove_100_000_files_0_bytes_0_depth.md` | Command | Mean [ms] | Min [ms] | Max [ms] | User [ms] | System [ms] | Relative | |:-----------------------------------------------|---------------:|---------:|---------:|----------:|------------:|------------:| | *`./target/release/rmz /tmp/ftzz`* | 830.7 ± 21.7 | 805.1 | 863.2 | 31.6 | 769.5 | 1.00 | | `./target/release/rm_stdlib /tmp/ftzz` | 833.8 ± 26.8 | 806.7 | 881.3 | 34.7 | 769.5 | 1.00 ± 0.04 | | `rm -r /tmp/ftzz` | 847.1 ± 15.3 | 833.9 | 871.3 | 53.1 | 775.2 | 1.02 ± 0.03 | | `find /tmp/ftzz -delete` | 849.7 ± 14.8 | 835.2 | 875.2 | 50.3 | 781.1 | 1.02 ± 0.03 | | `rsync --delete -r /tmp/empty/ /tmp/ftzz` | 978.3 ± 24.4 | 954.3 | 1033.6 | 89.4 | 819.5 | 1.18 ± 0.04 | | `perl -e 'for(</tmp/ftzz/*>){unlink}'` | 1070.0 ± 5.3 | 1063.0 | 1078.6 | 132.8 | 923.4 | 1.29 ± 0.03 | | `./target/release/rm_remove_dir_all /tmp/ftzz` | 1155.9 ± 42.9 | 1104.6 | 1256.0 | 124.8 | 984.6 | 1.39 ± 0.06 | | `./target/release/rm_rayon /tmp/ftzz` | 1193.3 ± 307.0 | 1082.7 | 2066.6 | 102.5 | 2030.4 | 1.44 ± 0.37 | #### `remove_100_000_files_0_bytes_5_files_per_dir.md` | Command | Mean [s] | Min [s] | Max [s] | User [s] | System [s] | Relative | |:-----------------------------------------------|--------------:|--------:|--------:|---------:|-----------:|------------:| | *`./target/release/rmz /tmp/ftzz`* | 0.640 ± 0.189 | 0.445 | 0.950 | 0.145 | 2.228 | 1.00 | | `./target/release/rm_rayon /tmp/ftzz` | 0.784 ± 0.110 | 0.670 | 0.961 | 0.303 | 2.610 | 1.22 ± 0.40 | | `./target/release/rm_stdlib /tmp/ftzz` | 0.988 ± 0.057 | 0.940 | 1.117 | 0.091 | 0.832 | 1.54 ± 0.46 | | `find /tmp/ftzz -delete` | 1.090 ± 0.021 | 1.056 | 1.136 | 0.164 | 0.883 | 1.70 ± 0.50 | | `rm -r /tmp/ftzz` | 1.123 ± 0.013 | 1.098 | 1.141 | 0.153 | 0.921 | 1.75 ± 0.52 | | `rsync --delete -r /tmp/empty/ /tmp/ftzz` | 1.190 ± 0.020 | 1.168 | 1.238 | 0.168 | 0.956 | 1.86 ± 0.55 | | `./target/release/rm_remove_dir_all /tmp/ftzz` | 1.340 ± 0.031 | 1.304 | 1.405 | 0.224 | 1.045 | 2.09 ± 0.62 | #### `remove_100_000_files_1G_bytes.md` | Command | Mean [s] | Min [s] | Max [s] | User [s] | System [s] | Relative | |:-----------------------------------------------|--------------:|--------:|--------:|---------:|-----------:|------------:| | *`./target/release/rmz /tmp/ftzz`* | 0.558 ± 0.129 | 0.400 | 0.734 | 0.063 | 2.339 | 1.00 | | `./target/release/rm_rayon /tmp/ftzz` | 0.625 ± 0.116 | 0.535 | 0.934 | 0.141 | 2.450 | 1.12 ± 0.33 | | `./target/release/rm_stdlib /tmp/ftzz` | 1.104 ± 0.014 | 1.080 | 1.132 | 0.043 | 1.020 | 1.98 ± 0.46 | | `find /tmp/ftzz -delete` | 1.128 ± 0.016 | 1.110 | 1.155 | 0.054 | 1.027 | 2.02 ± 0.47 | | `rm -r /tmp/ftzz` | 1.129 ± 0.008 | 1.117 | 1.142 | 0.054 | 1.035 | 2.02 ± 0.47 | | `rsync --delete -r /tmp/empty/ /tmp/ftzz` | 1.311 ± 0.005 | 1.301 | 1.317 | 0.098 | 1.138 | 2.35 ± 0.54 | | `./target/release/rm_remove_dir_all /tmp/ftzz` | 1.454 ± 0.014 | 1.434 | 1.478 | 0.143 | 1.261 | 2.60 ± 0.60 | #### `remove_100_000_files_1G_bytes_0_depth.md` | Command | Mean [s] | Min [s] | Max [s] | User [s] | System [s] | Relative | |:-----------------------------------------------|--------------:|--------:|--------:|---------:|-----------:|------------:| | `./target/release/rm_rayon /tmp/ftzz` | 1.087 ± 0.005 | 1.081 | 1.097 | 0.092 | 2.516 | 1.00 | | *`./target/release/rmz /tmp/ftzz`* | 1.115 ± 0.025 | 1.090 | 1.173 | 0.028 | 1.048 | 1.03 ± 0.02 | | `./target/release/rm_stdlib /tmp/ftzz` | 1.124 ± 0.038 | 1.094 | 1.208 | 0.034 | 1.044 | 1.03 ± 0.04 | | `rm -r /tmp/ftzz` | 1.129 ± 0.017 | 1.102 | 1.158 | 0.052 | 1.046 | 1.04 ± 0.02 | | `find /tmp/ftzz -delete` | 1.137 ± 0.011 | 1.120 | 1.156 | 0.057 | 1.048 | 1.05 ± 0.01 | | `rsync --delete -r /tmp/empty/ /tmp/ftzz` | 1.251 ± 0.019 | 1.235 | 1.301 | 0.087 | 1.093 | 1.15 ± 0.02 | | `perl -e 'for(</tmp/ftzz/*>){unlink}'` | 1.354 ± 0.008 | 1.342 | 1.367 | 0.130 | 1.192 | 1.24 ± 0.01 | | `./target/release/rm_remove_dir_all /tmp/ftzz` | 1.470 ± 0.031 | 1.438 | 1.537 | 0.127 | 1.285 | 1.35 ± 0.03 | #### `remove_100_000_files_1G_bytes_5_files_per_dir.md` | Command | Mean [s] | Min [s] | Max [s] | User [s] | System [s] | Relative | |:-----------------------------------------------|--------------:|--------:|--------:|---------:|-----------:|------------:| | *`./target/release/rmz /tmp/ftzz`* | 0.508 ± 0.034 | 0.483 | 0.600 | 0.115 | 2.352 | 1.00 | | `./target/release/rm_rayon /tmp/ftzz` | 0.683 ± 0.059 | 0.599 | 0.779 | 0.261 | 2.828 | 1.34 ± 0.15 | | `./target/release/rm_stdlib /tmp/ftzz` | 1.246 ± 0.033 | 1.209 | 1.327 | 0.102 | 1.082 | 2.45 ± 0.18 | | `find /tmp/ftzz -delete` | 1.387 ± 0.034 | 1.354 | 1.462 | 0.169 | 1.159 | 2.73 ± 0.19 | | `rm -r /tmp/ftzz` | 1.402 ± 0.023 | 1.372 | 1.451 | 0.163 | 1.182 | 2.76 ± 0.19 | | `rsync --delete -r /tmp/empty/ /tmp/ftzz` | 1.485 ± 0.012 | 1.460 | 1.501 | 0.168 | 1.237 | 2.92 ± 0.20 | | `./target/release/rm_remove_dir_all /tmp/ftzz` | 1.632 ± 0.026 | 1.598 | 1.689 | 0.230 | 1.338 | 3.21 ± 0.22 | #### `remove_10_000_files_0_bytes.md` | Command | Mean [ms] | Min [ms] | Max [ms] | User [ms] | System [ms] | Relative | |:-----------------------------------------------|------------:|---------:|---------:|----------:|------------:|------------:| | *`./target/release/rmz /tmp/ftzz`* | 33.9 ± 1.1 | 32.1 | 36.3 | 9.2 | 145.0 | 1.00 | | `./target/release/rm_rayon /tmp/ftzz` | 40.1 ± 2.3 | 37.2 | 48.1 | 18.6 | 160.0 | 1.18 ± 0.08 | | `./target/release/rm_stdlib /tmp/ftzz` | 77.2 ± 1.3 | 75.3 | 80.0 | 7.2 | 68.4 | 2.28 ± 0.08 | | `find /tmp/ftzz -delete` | 85.4 ± 1.3 | 84.0 | 88.5 | 10.1 | 73.0 | 2.52 ± 0.09 | | `rm -r /tmp/ftzz` | 86.1 ± 1.5 | 84.8 | 89.2 | 11.3 | 73.0 | 2.54 ± 0.09 | | `./target/release/rm_remove_dir_all /tmp/ftzz` | 110.8 ± 1.5 | 109.1 | 113.8 | 18.2 | 90.2 | 3.27 ± 0.12 | | `rsync --delete -r /tmp/empty/ /tmp/ftzz` | 138.6 ± 1.3 | 136.8 | 143.0 | 14.1 | 83.7 | 4.09 ± 0.14 | #### `remove_10_000_files_1G_bytes.md` | Command | Mean [ms] | Min [ms] | Max [ms] | User [ms] | System [ms] | Relative | |:-----------------------------------------------|------------:|---------:|---------:|----------:|------------:|------------:| | *`./target/release/rmz /tmp/ftzz`* | 45.4 ± 1.8 | 43.7 | 50.0 | 7.3 | 297.6 | 1.00 | | `./target/release/rm_rayon /tmp/ftzz` | 51.6 ± 1.2 | 50.0 | 53.8 | 18.6 | 310.4 | 1.14 ± 0.05 | | `./target/release/rm_stdlib /tmp/ftzz` | 171.2 ± 1.5 | 168.9 | 173.8 | 5.5 | 162.0 | 3.77 ± 0.15 | | `find /tmp/ftzz -delete` | 179.9 ± 0.9 | 178.7 | 181.4 | 12.2 | 163.8 | 3.96 ± 0.16 | | `rm -r /tmp/ftzz` | 181.5 ± 1.3 | 180.1 | 184.6 | 12.4 | 165.4 | 4.00 ± 0.16 | | `./target/release/rm_remove_dir_all /tmp/ftzz` | 208.0 ± 1.5 | 206.0 | 209.9 | 17.3 | 186.1 | 4.58 ± 0.19 | | `rsync --delete -r /tmp/empty/ /tmp/ftzz` | 233.8 ± 0.9 | 232.5 | 235.1 | 14.4 | 176.7 | 5.15 ± 0.21 | #### `remove_10_files_0_bytes.md` | Command | Mean [ms] | Min [ms] | Max [ms] | User [ms] | System [ms] | Relative | |:-----------------------------------------------|-----------:|---------:|---------:|----------:|------------:|-------------:| | *`./target/release/rmz /tmp/ftzz`* | 1.1 ± 0.1 | 1.0 | 1.4 | 0.6 | 1.6 | 1.00 | | `./target/release/rm_remove_dir_all /tmp/ftzz` | 1.3 ± 0.0 | 1.2 | 1.4 | 0.4 | 0.8 | 1.18 ± 0.07 | | `./target/release/rm_rayon /tmp/ftzz` | 1.5 ± 0.4 | 1.2 | 3.5 | 1.2 | 2.9 | 1.39 ± 0.34 | | `./target/release/rm_stdlib /tmp/ftzz` | 2.8 ± 0.1 | 2.5 | 3.2 | 0.7 | 1.9 | 2.53 ± 0.16 | | `rm -r /tmp/ftzz` | 3.5 ± 0.1 | 3.3 | 3.7 | 1.0 | 2.3 | 3.25 ± 0.20 | | `find /tmp/ftzz -delete` | 4.2 ± 0.3 | 3.5 | 4.8 | 1.0 | 2.6 | 3.85 ± 0.38 | | `rsync --delete -r /tmp/empty/ /tmp/ftzz` | 46.8 ± 0.1 | 46.5 | 47.0 | 2.4 | 5.1 | 42.89 ± 2.40 | #### `remove_10_files_1G_bytes.md` | Command | Mean [ms] | Min [ms] | Max [ms] | User [ms] | System [ms] | Relative | |:-----------------------------------------------|-----------:|---------:|---------:|----------:|------------:|------------:| | `./target/release/rm_rayon /tmp/ftzz` | 15.9 ± 1.1 | 14.1 | 17.7 | 1.0 | 69.2 | 1.00 | | *`./target/release/rmz /tmp/ftzz`* | 17.3 ± 0.6 | 16.2 | 18.3 | 0.7 | 61.5 | 1.09 ± 0.08 | | `./target/release/rm_stdlib /tmp/ftzz` | 48.4 ± 0.3 | 47.4 | 49.1 | 0.3 | 47.4 | 3.04 ± 0.20 | | `./target/release/rm_remove_dir_all /tmp/ftzz` | 48.5 ± 0.3 | 47.5 | 48.8 | 0.3 | 47.6 | 3.05 ± 0.20 | | `rm -r /tmp/ftzz` | 49.0 ± 1.9 | 48.0 | 57.7 | 0.5 | 47.7 | 3.08 ± 0.24 | | `find /tmp/ftzz -delete` | 51.2 ± 8.9 | 48.9 | 91.1 | 0.3 | 48.0 | 3.22 ± 0.60 | | `rsync --delete -r /tmp/empty/ /tmp/ftzz` | 90.3 ± 0.2 | 90.0 | 90.8 | 1.5 | 48.5 | 5.68 ± 0.38 | ## Copy ### Setup ```bash cargo install fcp xcp git clone https://github.com/wheybags/wcp.git && mkdir wcp/build && (cd wcp/build && cmake .. -DCMAKE_BUILD_TYPE=Release && make) ``` ### Run ```bash for num_bytes in {0,1G}; do for num_files in {10,10_000,100_000,1M}; do hyperfine --warmup 3 -N \ --export-markdown "benches/copy_${num_files}_files_${num_bytes}_bytes.md" \ --export-json "benches/copy_${num_files}_files_${num_bytes}_bytes.json" \ --setup "ftzz g -n ${num_files} -b ${num_bytes} /tmp/ftzz" \ --prepare "rm -rf /tmp/ftzzz" --cleanup "rm -r /tmp/ftzz" \ "cp -r /tmp/ftzz /tmp/ftzzz" \ "fcp /tmp/ftzz /tmp/ftzzz" \ "xcp -r /tmp/ftzz /tmp/ftzzz" \ "./wcp/build/wcp /tmp/ftzz /tmp/ftzzz" \ "rsync -rlp --inplace /tmp/ftzz /tmp/ftzzz" \ "sh -c '(cd /tmp/ftzz; tar cf - .) | (mkdir /tmp/ftzzz; cd /tmp/ftzzz; tar xf -)'" \ "./target/release/cp_stdlib /tmp/ftzz /tmp/ftzzz" \ "./target/release/cp_rayon /tmp/ftzz /tmp/ftzzz" \ "./target/release/cpz /tmp/ftzz /tmp/ftzzz" done hyperfine --warmup 3 -N \ --export-markdown "benches/copy_100_000_files_${num_bytes}_bytes_0_depth.md" \ --export-json "benches/copy_100_000_files_${num_bytes}_bytes_0_depth.json" \ --setup "ftzz g -n 100_000 -b ${num_bytes} -d 0 /tmp/ftzz" \ --prepare "rm -rf /tmp/ftzzz" --cleanup "rm -r /tmp/ftzz" \ "cp -r /tmp/ftzz /tmp/ftzzz" \ "fcp /tmp/ftzz /tmp/ftzzz" \ "xcp -r /tmp/ftzz /tmp/ftzzz" \ "./wcp/build/wcp /tmp/ftzz /tmp/ftzzz" \ "rsync -rlp --inplace /tmp/ftzz /tmp/ftzzz" \ "sh -c '(cd /tmp/ftzz; tar cf - .) | (mkdir /tmp/ftzzz; cd /tmp/ftzzz; tar xf -)'" \ "./target/release/cp_stdlib /tmp/ftzz /tmp/ftzzz" \ "./target/release/cp_rayon /tmp/ftzz /tmp/ftzzz" \ "./target/release/cpz /tmp/ftzz /tmp/ftzzz" hyperfine --warmup 3 -N \ --export-markdown "benches/copy_100_000_files_${num_bytes}_bytes_5_files_per_dir.md" \ --export-json "benches/copy_100_000_files_${num_bytes}_bytes_5_files_per_dir.json" \ --setup "ftzz g -n 100_000 -b ${num_bytes} -r 5 /tmp/ftzz" \ --prepare "rm -rf /tmp/ftzzz" --cleanup "rm -r /tmp/ftzz" \ "cp -r /tmp/ftzz /tmp/ftzzz" \ "fcp /tmp/ftzz /tmp/ftzzz" \ "xcp -r /tmp/ftzz /tmp/ftzzz" \ "./wcp/build/wcp /tmp/ftzz /tmp/ftzzz" \ "rsync -rlp --inplace /tmp/ftzz /tmp/ftzzz" \ "sh -c '(cd /tmp/ftzz; tar cf - .) | (mkdir /tmp/ftzzz; cd /tmp/ftzzz; tar xf -)'" \ "./target/release/cp_stdlib /tmp/ftzz /tmp/ftzzz" \ "./target/release/cp_rayon /tmp/ftzz /tmp/ftzzz" \ "./target/release/cpz /tmp/ftzz /tmp/ftzzz" done hyperfine --warmup 3 -N \ --export-markdown "benches/copy_100_000_files_1G_bytes_0_depth_0_entropy.md" \ --export-json "benches/copy_100_000_files_1G_bytes_0_depth_0_entropy.json" \ --setup "ftzz g -n 100_000 -b 1G -d 0 --fill-byte 0 /tmp/ftzz" \ --prepare "rm -rf /tmp/ftzzz" --cleanup "rm -r /tmp/ftzz" \ "cp -r /tmp/ftzz /tmp/ftzzz" \ "fcp /tmp/ftzz /tmp/ftzzz" \ "xcp -r /tmp/ftzz /tmp/ftzzz" \ "./wcp/build/wcp /tmp/ftzz /tmp/ftzzz" \ "rsync -rlp --inplace /tmp/ftzz /tmp/ftzzz" \ "sh -c '(cd /tmp/ftzz; tar cf - .) | (mkdir /tmp/ftzzz; cd /tmp/ftzzz; tar xf -)'" \ "./target/release/cp_stdlib /tmp/ftzz /tmp/ftzzz" \ "./target/release/cp_rayon /tmp/ftzz /tmp/ftzzz" \ "./target/release/cpz /tmp/ftzz /tmp/ftzzz" ``` ### Results #### `copy_1M_files_0_bytes.md` | Command | Mean [s] | Min [s] | Max [s] | User [s] | System [s] | Relative | |:------------------------------------------------------------------------------------|---------------:|--------:|--------:|---------:|-----------:|------------:| | *`./target/release/cpz /tmp/ftzz /tmp/ftzzz`* | 5.076 ± 0.976 | 4.283 | 6.700 | 2.769 | 23.713 | 1.00 | | `./target/release/cp_rayon /tmp/ftzz /tmp/ftzzz` | 5.854 ± 0.310 | 5.542 | 6.429 | 5.287 | 29.890 | 1.15 ± 0.23 | | `fcp /tmp/ftzz /tmp/ftzzz` | 11.801 ± 0.085 | 11.727 | 11.993 | 5.376 | 59.341 | 2.33 ± 0.45 | | `xcp -r /tmp/ftzz /tmp/ftzzz` | 11.829 ± 0.066 | 11.733 | 11.920 | 5.420 | 39.040 | 2.33 ± 0.45 | | `sh -c '(cd /tmp/ftzz; tar cf - .) \| (mkdir /tmp/ftzzz; cd /tmp/ftzzz; tar xf -)'` | 15.019 ± 0.079 | 14.915 | 15.131 | 3.893 | 14.808 | 2.96 ± 0.57 | | `cp -r /tmp/ftzz /tmp/ftzzz` | 18.436 ± 0.097 | 18.313 | 18.622 | 2.836 | 15.261 | 3.63 ± 0.70 | | `./target/release/cp_stdlib /tmp/ftzz /tmp/ftzzz` | 19.496 ± 0.090 | 19.346 | 19.609 | 3.217 | 15.928 | 3.84 ± 0.74 | | `rsync -rlp --inplace /tmp/ftzz /tmp/ftzzz` | 28.195 ± 0.159 | 27.929 | 28.439 | 6.635 | 23.035 | 5.55 ± 1.07 | #### `copy_1M_files_1G_bytes.md` | Command | Mean [s] | Min [s] | Max [s] | User [s] | System [s] | Relative | |:------------------------------------------------------------------------------------|---------------:|--------:|--------:|---------:|-----------:|-------------:| | *`./target/release/cpz /tmp/ftzz /tmp/ftzzz`* | 7.593 ± 0.735 | 7.141 | 9.648 | 3.151 | 40.109 | 1.00 | | `./target/release/cp_rayon /tmp/ftzz /tmp/ftzzz` | 8.827 ± 0.796 | 8.104 | 10.143 | 5.482 | 46.376 | 1.16 ± 0.15 | | `xcp -r /tmp/ftzz /tmp/ftzzz` | 13.374 ± 0.149 | 13.103 | 13.559 | 6.218 | 46.409 | 1.76 ± 0.17 | | `fcp /tmp/ftzz /tmp/ftzzz` | 13.459 ± 0.087 | 13.349 | 13.636 | 5.837 | 68.041 | 1.77 ± 0.17 | | `sh -c '(cd /tmp/ftzz; tar cf - .) \| (mkdir /tmp/ftzzz; cd /tmp/ftzzz; tar xf -)'` | 23.669 ± 1.371 | 21.564 | 25.593 | 5.858 | 22.336 | 3.12 ± 0.35 | | `cp -r /tmp/ftzz /tmp/ftzzz` | 28.173 ± 0.091 | 28.047 | 28.343 | 2.950 | 24.495 | 3.71 ± 0.36 | | `./target/release/cp_stdlib /tmp/ftzz /tmp/ftzzz` | 28.945 ± 0.121 | 28.744 | 29.084 | 3.394 | 24.931 | 3.81 ± 0.37 | | `rsync -rlp --inplace /tmp/ftzz /tmp/ftzzz` | 89.173 ± 1.821 | 87.317 | 94.034 | 12.557 | 55.997 | 11.74 ± 1.16 | #### `copy_100_000_files_0_bytes.md` | Command | Mean [s] | Min [s] | Max [s] | User [s] | System [s] | Relative | |:------------------------------------------------------------------------------------|--------------:|--------:|--------:|---------:|-----------:|------------:| | *`./target/release/cpz /tmp/ftzz /tmp/ftzzz`* | 0.379 ± 0.009 | 0.373 | 0.397 | 0.261 | 1.910 | 1.00 | | `./target/release/cp_rayon /tmp/ftzz /tmp/ftzzz` | 0.487 ± 0.018 | 0.471 | 0.535 | 0.513 | 2.604 | 1.28 ± 0.06 | | `fcp /tmp/ftzz /tmp/ftzzz` | 0.597 ± 0.016 | 0.581 | 0.629 | 0.554 | 3.073 | 1.57 ± 0.05 | | `xcp -r /tmp/ftzz /tmp/ftzzz` | 0.957 ± 0.015 | 0.939 | 0.982 | 0.529 | 3.295 | 2.52 ± 0.07 | | `sh -c '(cd /tmp/ftzz; tar cf - .) \| (mkdir /tmp/ftzzz; cd /tmp/ftzzz; tar xf -)'` | 1.302 ± 0.022 | 1.277 | 1.332 | 0.389 | 1.274 | 3.43 ± 0.10 | | `cp -r /tmp/ftzz /tmp/ftzzz` | 1.632 ± 0.023 | 1.611 | 1.682 | 0.277 | 1.323 | 4.30 ± 0.11 | | `./target/release/cp_stdlib /tmp/ftzz /tmp/ftzzz` | 1.730 ± 0.009 | 1.719 | 1.750 | 0.308 | 1.389 | 4.56 ± 0.11 | | `rsync -rlp --inplace /tmp/ftzz /tmp/ftzzz` | 2.838 ± 0.019 | 2.803 | 2.864 | 0.638 | 2.141 | 7.49 ± 0.18 | #### `copy_100_000_files_0_bytes_0_depth.md` | Command | Mean [s] | Min [s] | Max [s] | User [s] | System [s] | Relative | |:------------------------------------------------------------------------------------|--------------:|--------:|--------:|---------:|-----------:|------------:| | `xcp -r /tmp/ftzz /tmp/ftzzz` | 1.133 ± 0.014 | 1.109 | 1.161 | 0.474 | 3.606 | 1.00 | | `fcp /tmp/ftzz /tmp/ftzzz` | 1.260 ± 0.010 | 1.247 | 1.280 | 0.412 | 6.386 | 1.11 ± 0.02 | | `./target/release/cp_rayon /tmp/ftzz /tmp/ftzzz` | 1.270 ± 0.010 | 1.256 | 1.287 | 0.390 | 6.620 | 1.12 ± 0.02 | | *`./target/release/cpz /tmp/ftzz /tmp/ftzzz`* | 1.365 ± 0.005 | 1.357 | 1.373 | 0.167 | 1.171 | 1.20 ± 0.02 | | `sh -c '(cd /tmp/ftzz; tar cf - .) \| (mkdir /tmp/ftzzz; cd /tmp/ftzzz; tar xf -)'` | 1.406 ± 0.012 | 1.389 | 1.428 | 0.339 | 1.394 | 1.24 ± 0.02 | | `cp -r /tmp/ftzz /tmp/ftzzz` | 1.618 ± 0.012 | 1.594 | 1.636 | 0.255 | 1.335 | 1.43 ± 0.02 | | `./target/release/cp_stdlib /tmp/ftzz /tmp/ftzzz` | 1.735 ± 0.008 | 1.725 | 1.751 | 0.287 | 1.418 | 1.53 ± 0.02 | | `rsync -rlp --inplace /tmp/ftzz /tmp/ftzzz` | 2.157 ± 0.030 | 2.117 | 2.216 | 0.581 | 2.057 | 1.90 ± 0.04 | #### `copy_100_000_files_0_bytes_5_files_per_dir.md` | Command | Mean [s] | Min [s] | Max [s] | User [s] | System [s] | Relative | |:------------------------------------------------------------------------------------|--------------:|--------:|--------:|---------:|-----------:|------------:| | *`./target/release/cpz /tmp/ftzz /tmp/ftzzz`* | 0.413 ± 0.013 | 0.404 | 0.440 | 0.318 | 2.214 | 1.00 | | `./target/release/cp_rayon /tmp/ftzz /tmp/ftzzz` | 0.493 ± 0.008 | 0.484 | 0.509 | 0.576 | 2.661 | 1.19 ± 0.04 | | `fcp /tmp/ftzz /tmp/ftzzz` | 0.505 ± 0.009 | 0.496 | 0.523 | 0.672 | 2.700 | 1.22 ± 0.05 | | `xcp -r /tmp/ftzz /tmp/ftzzz` | 0.844 ± 0.033 | 0.804 | 0.897 | 0.723 | 2.841 | 2.04 ± 0.10 | | `sh -c '(cd /tmp/ftzz; tar cf - .) \| (mkdir /tmp/ftzzz; cd /tmp/ftzzz; tar xf -)'` | 1.319 ± 0.018 | 1.297 | 1.350 | 0.463 | 1.371 | 3.19 ± 0.11 | | `cp -r /tmp/ftzz /tmp/ftzzz` | 1.737 ± 0.008 | 1.729 | 1.757 | 0.318 | 1.385 | 4.20 ± 0.14 | | `./target/release/cp_stdlib /tmp/ftzz /tmp/ftzzz` | 1.791 ± 0.012 | 1.767 | 1.805 | 0.336 | 1.419 | 4.33 ± 0.14 | | `rsync -rlp --inplace /tmp/ftzz /tmp/ftzzz` | 3.028 ± 0.023 | 2.991 | 3.064 | 0.787 | 2.217 | 7.33 ± 0.24 | #### `copy_100_000_files_1G_bytes.md` | Command | Mean [s] | Min [s] | Max [s] | User [s] | System [s] | Relative | |:------------------------------------------------------------------------------------|---------------:|--------:|--------:|---------:|-----------:|-------------:| | *`./target/release/cpz /tmp/ftzz /tmp/ftzzz`* | 0.711 ± 0.129 | 0.618 | 1.064 | 0.317 | 3.421 | 1.00 | | `./target/release/cp_rayon /tmp/ftzz /tmp/ftzzz` | 0.819 ± 0.042 | 0.763 | 0.878 | 0.551 | 4.251 | 1.15 ± 0.22 | | `fcp /tmp/ftzz /tmp/ftzzz` | 0.858 ± 0.030 | 0.811 | 0.897 | 0.591 | 4.541 | 1.21 ± 0.22 | | `xcp -r /tmp/ftzz /tmp/ftzzz` | 1.110 ± 0.016 | 1.079 | 1.129 | 0.610 | 3.930 | 1.56 ± 0.28 | | `sh -c '(cd /tmp/ftzz; tar cf - .) \| (mkdir /tmp/ftzzz; cd /tmp/ftzzz; tar xf -)'` | 2.437 ± 0.061 | 2.377 | 2.565 | 0.726 | 2.784 | 3.43 ± 0.63 | | `cp -r /tmp/ftzz /tmp/ftzzz` | 2.453 ± 0.016 | 2.430 | 2.478 | 0.289 | 2.108 | 3.45 ± 0.63 | | `./target/release/cp_stdlib /tmp/ftzz /tmp/ftzzz` | 2.550 ± 0.029 | 2.513 | 2.616 | 0.331 | 2.161 | 3.59 ± 0.65 | | `rsync -rlp --inplace /tmp/ftzz /tmp/ftzzz` | 10.653 ± 0.120 | 10.454 | 10.842 | 4.030 | 6.585 | 14.99 ± 2.73 | #### `copy_100_000_files_1G_bytes_0_depth.md` | Command | Mean [s] | Min [s] | Max [s] | User [s] | System [s] | Relative | |:------------------------------------------------------------------------------------|--------------:|--------:|--------:|---------:|-----------:|------------:| | `xcp -r /tmp/ftzz /tmp/ftzzz` | 1.209 ± 0.020 | 1.181 | 1.244 | 0.524 | 4.290 | 1.00 | | `./target/release/cp_rayon /tmp/ftzz /tmp/ftzzz` | 1.340 ± 0.005 | 1.334 | 1.350 | 0.402 | 6.999 | 1.11 ± 0.02 | | `fcp /tmp/ftzz /tmp/ftzzz` | 1.358 ± 0.015 | 1.338 | 1.391 | 0.432 | 6.779 | 1.12 ± 0.02 | | *`./target/release/cpz /tmp/ftzz /tmp/ftzzz`* | 2.080 ± 0.012 | 2.064 | 2.099 | 0.206 | 1.820 | 1.72 ± 0.03 | | `./target/release/cp_stdlib /tmp/ftzz /tmp/ftzzz` | 2.371 ± 0.020 | 2.348 | 2.408 | 0.288 | 2.030 | 1.96 ± 0.04 | | `sh -c '(cd /tmp/ftzz; tar cf - .) \| (mkdir /tmp/ftzzz; cd /tmp/ftzzz; tar xf -)'` | 2.422 ± 0.026 | 2.386 | 2.477 | 0.638 | 2.749 | 2.00 ± 0.04 | | `cp -r /tmp/ftzz /tmp/ftzzz` | 2.464 ± 0.037 | 2.432 | 2.541 | 0.268 | 2.133 | 2.04 ± 0.05 | | `rsync -rlp --inplace /tmp/ftzz /tmp/ftzzz` | 9.292 ± 0.080 | 9.164 | 9.382 | 3.508 | 5.456 | 7.69 ± 0.14 | #### `copy_100_000_files_1G_bytes_0_depth_0_entropy.md` | Command | Mean [s] | Min [s] | Max [s] | User [s] | System [s] | Relative | |:------------------------------------------------------------------------------------|--------------:|--------:|--------:|---------:|-----------:|------------:| | `xcp -r /tmp/ftzz /tmp/ftzzz` | 1.200 ± 0.018 | 1.178 | 1.230 | 0.539 | 4.253 | 1.00 | | `./target/release/cp_rayon /tmp/ftzz /tmp/ftzzz` | 1.346 ± 0.010 | 1.332 | 1.361 | 0.407 | 7.095 | 1.12 ± 0.02 | | `fcp /tmp/ftzz /tmp/ftzzz` | 1.409 ± 0.033 | 1.345 | 1.451 | 0.448 | 6.888 | 1.17 ± 0.03 | | *`./target/release/cpz /tmp/ftzz /tmp/ftzzz`* | 2.086 ± 0.015 | 2.057 | 2.104 | 0.212 | 1.819 | 1.74 ± 0.03 | | `cp -r /tmp/ftzz /tmp/ftzzz` | 2.331 ± 0.019 | 2.308 | 2.366 | 0.275 | 2.004 | 1.94 ± 0.03 | | `./target/release/cp_stdlib /tmp/ftzz /tmp/ftzzz` | 2.364 ± 0.008 | 2.356 | 2.380 | 0.296 | 2.013 | 1.97 ± 0.03 | | `sh -c '(cd /tmp/ftzz; tar cf - .) \| (mkdir /tmp/ftzzz; cd /tmp/ftzzz; tar xf -)'` | 2.406 ± 0.034 | 2.366 | 2.488 | 0.628 | 2.734 | 2.00 ± 0.04 | | `rsync -rlp --inplace /tmp/ftzz /tmp/ftzzz` | 9.245 ± 0.059 | 9.144 | 9.332 | 3.526 | 5.482 | 7.70 ± 0.13 | #### `copy_100_000_files_1G_bytes_5_files_per_dir.md` | Command | Mean [s] | Min [s] | Max [s] | User [s] | System [s] | Relative | |:------------------------------------------------------------------------------------|--------------:|--------:|--------:|---------:|-----------:|-------------:| | `./target/release/cp_rayon /tmp/ftzz /tmp/ftzzz` | 0.733 ± 0.028 | 0.714 | 0.812 | 0.598 | 3.970 | 1.00 | | `fcp /tmp/ftzz /tmp/ftzzz` | 0.766 ± 0.027 | 0.742 | 0.826 | 0.695 | 4.110 | 1.05 ± 0.05 | | *`./target/release/cpz /tmp/ftzz /tmp/ftzzz`* | 0.775 ± 0.063 | 0.691 | 0.880 | 0.385 | 3.810 | 1.06 ± 0.10 | | `xcp -r /tmp/ftzz /tmp/ftzzz` | 1.050 ± 0.025 | 1.021 | 1.094 | 0.745 | 3.788 | 1.43 ± 0.07 | | `sh -c '(cd /tmp/ftzz; tar cf - .) \| (mkdir /tmp/ftzzz; cd /tmp/ftzzz; tar xf -)'` | 2.359 ± 0.033 | 2.324 | 2.406 | 0.771 | 2.752 | 3.22 ± 0.13 | | `cp -r /tmp/ftzz /tmp/ftzzz` | 2.506 ± 0.017 | 2.480 | 2.526 | 0.338 | 2.110 | 3.42 ± 0.13 | | `./target/release/cp_stdlib /tmp/ftzz /tmp/ftzzz` | 2.540 ± 0.018 | 2.501 | 2.559 | 0.366 | 2.116 | 3.46 ± 0.14 | | `rsync -rlp --inplace /tmp/ftzz /tmp/ftzzz` | 9.930 ± 0.168 | 9.748 | 10.341 | 4.235 | 6.487 | 13.54 ± 0.57 | #### `copy_10_000_files_0_bytes.md` | Command | Mean [ms] | Min [ms] | Max [ms] | User [ms] | System [ms] | Relative | |:------------------------------------------------------------------------------------|------------:|---------:|---------:|----------:|------------:|------------:| | *`./target/release/cpz /tmp/ftzz /tmp/ftzzz`* | 33.6 ± 0.4 | 33.1 | 34.7 | 28.8 | 186.4 | 1.00 | | `./target/release/cp_rayon /tmp/ftzz /tmp/ftzzz` | 43.7 ± 0.5 | 42.9 | 45.3 | 55.6 | 243.4 | 1.30 ± 0.02 | | `fcp /tmp/ftzz /tmp/ftzzz` | 45.7 ± 0.7 | 44.9 | 48.0 | 61.5 | 249.9 | 1.36 ± 0.03 | | `xcp -r /tmp/ftzz /tmp/ftzzz` | 78.2 ± 5.3 | 71.6 | 96.4 | 59.0 | 271.6 | 2.33 ± 0.16 | | `sh -c '(cd /tmp/ftzz; tar cf - .) \| (mkdir /tmp/ftzzz; cd /tmp/ftzzz; tar xf -)'` | 119.5 ± 1.7 | 116.7 | 122.6 | 44.7 | 118.7 | 3.55 ± 0.07 | | `cp -r /tmp/ftzz /tmp/ftzzz` | 159.8 ± 2.5 | 156.7 | 164.3 | 29.7 | 126.2 | 4.75 ± 0.09 | | `./target/release/cp_stdlib /tmp/ftzz /tmp/ftzzz` | 164.0 ± 1.5 | 162.2 | 167.9 | 31.2 | 129.0 | 4.87 ± 0.07 | | `rsync -rlp --inplace /tmp/ftzz /tmp/ftzzz` | 321.1 ± 3.0 | 316.5 | 324.9 | 73.3 | 208.2 | 9.55 ± 0.15 | #### `copy_10_000_files_1G_bytes.md` | Command | Mean [ms] | Min [ms] | Max [ms] | User [ms] | System [ms] | Relative | |:------------------------------------------------------------------------------------|--------------:|---------:|---------:|----------:|------------:|-------------:| | *`./target/release/cpz /tmp/ftzz /tmp/ftzzz`* | 55.0 ± 2.9 | 52.0 | 59.7 | 34.0 | 313.3 | 1.00 | | `./target/release/cp_rayon /tmp/ftzz /tmp/ftzzz` | 66.2 ± 3.7 | 62.9 | 72.7 | 55.6 | 374.0 | 1.20 ± 0.09 | | `fcp /tmp/ftzz /tmp/ftzzz` | 67.9 ± 4.0 | 63.7 | 75.4 | 63.6 | 380.9 | 1.23 ± 0.10 | | `xcp -r /tmp/ftzz /tmp/ftzzz` | 142.2 ± 44.9 | 93.2 | 197.6 | 72.8 | 379.1 | 2.59 ± 0.83 | | `cp -r /tmp/ftzz /tmp/ftzzz` | 231.3 ± 2.5 | 228.8 | 235.2 | 30.9 | 194.4 | 4.21 ± 0.23 | | `./target/release/cp_stdlib /tmp/ftzz /tmp/ftzzz` | 234.7 ± 2.7 | 231.4 | 240.0 | 36.0 | 192.6 | 4.27 ± 0.23 | | `sh -c '(cd /tmp/ftzz; tar cf - .) \| (mkdir /tmp/ftzzz; cd /tmp/ftzzz; tar xf -)'` | 676.7 ± 4.2 | 670.8 | 683.0 | 185.0 | 915.5 | 12.31 ± 0.66 | | `rsync -rlp --inplace /tmp/ftzz /tmp/ftzzz` | 2572.6 ± 26.8 | 2535.0 | 2626.5 | 2714.9 | 1197.5 | 46.78 ± 2.55 | #### `copy_10_files_0_bytes.md` | Command | Mean [ms] | Min [ms] | Max [ms] | User [ms] | System [ms] | Relative | |:------------------------------------------------------------------------------------|-----------:|---------:|---------:|----------:|------------:|-------------:| | `./target/release/cp_rayon /tmp/ftzz /tmp/ftzzz` | 2.7 ± 0.3 | 2.3 | 3.7 | 2.3 | 5.2 | 1.00 | | `./target/release/cp_stdlib /tmp/ftzz /tmp/ftzzz` | 2.7 ± 0.1 | 2.6 | 3.3 | 0.7 | 1.8 | 1.01 ± 0.11 | | `fcp /tmp/ftzz /tmp/ftzzz` | 2.8 ± 0.1 | 2.5 | 3.2 | 2.2 | 5.4 | 1.03 ± 0.13 | | *`./target/release/cpz /tmp/ftzz /tmp/ftzzz`* | 2.9 ± 0.2 | 2.6 | 3.5 | 1.4 | 4.8 | 1.08 ± 0.14 | | `cp -r /tmp/ftzz /tmp/ftzzz` | 3.4 ± 0.4 | 3.0 | 4.0 | 0.9 | 2.3 | 1.26 ± 0.20 | | `xcp -r /tmp/ftzz /tmp/ftzzz` | 3.6 ± 0.4 | 3.2 | 4.6 | 1.7 | 3.2 | 1.33 ± 0.21 | | `sh -c '(cd /tmp/ftzz; tar cf - .) \| (mkdir /tmp/ftzzz; cd /tmp/ftzzz; tar xf -)'` | 9.6 ± 1.4 | 6.7 | 10.9 | 6.6 | 7.8 | 3.56 ± 0.64 | | `rsync -rlp --inplace /tmp/ftzz /tmp/ftzzz` | 47.7 ± 0.1 | 47.2 | 48.0 | 3.2 | 5.7 | 17.77 ± 1.96 | #### `copy_10_files_1G_bytes.md` | Command | Mean [ms] | Min [ms] | Max [ms] | User [ms] | System [ms] | Relative | |:------------------------------------------------------------------------------------|-------------:|---------:|---------:|----------:|------------:|---------------:| | *`./target/release/cpz /tmp/ftzz /tmp/ftzzz`* | 1.3 ± 0.2 | 1.1 | 2.2 | 0.8 | 2.1 | 1.00 | | `fcp /tmp/ftzz /tmp/ftzzz` | 1.5 ± 0.1 | 1.3 | 1.9 | 1.1 | 3.1 | 1.11 ± 0.15 | | `./target/release/cp_rayon /tmp/ftzz /tmp/ftzzz` | 1.9 ± 0.2 | 1.5 | 7.6 | 1.4 | 3.9 | 1.39 ± 0.23 | | `cp -r /tmp/ftzz /tmp/ftzzz` | 1.9 ± 0.4 | 1.6 | 2.7 | 0.5 | 1.3 | 1.40 ± 0.31 | | `./target/release/cp_stdlib /tmp/ftzz /tmp/ftzzz` | 3.0 ± 0.1 | 2.8 | 3.2 | 0.7 | 2.1 | 2.23 ± 0.26 | | `xcp -r /tmp/ftzz /tmp/ftzzz` | 3.4 ± 0.4 | 2.7 | 11.4 | 1.8 | 3.6 | 2.55 ± 0.40 | | `sh -c '(cd /tmp/ftzz; tar cf - .) \| (mkdir /tmp/ftzzz; cd /tmp/ftzzz; tar xf -)'` | 304.3 ± 14.9 | 297.9 | 346.4 | 83.4 | 449.5 | 225.47 ± 27.89 | | `rsync -rlp --inplace /tmp/ftzz /tmp/ftzzz` | 1042.4 ± 1.7 | 1040.5 | 1045.6 | 1529.1 | 350.0 | 772.37 ± 87.80 | 0707010000000B000041ED0000000000000000000000026682237800000000000000000000000000000000000000000000001F00000000fuc-2.2.0/comparisons/cp_rayon0707010000000C000081A40000000000000000000000016682237800000078000000000000000000000000000000000000002A00000000fuc-2.2.0/comparisons/cp_rayon/Cargo.toml[package] name = "cp_rayon" version = "0.0.0" edition.workspace = true publish = false [dependencies] rayon = "1.10.0" 0707010000000D000041ED0000000000000000000000026682237800000000000000000000000000000000000000000000002300000000fuc-2.2.0/comparisons/cp_rayon/src0707010000000E000081A40000000000000000000000016682237800000289000000000000000000000000000000000000002A00000000fuc-2.2.0/comparisons/cp_rayon/src/lib.rs#![allow(clippy::pedantic)] use std::{fs, io, path::Path}; use rayon::prelude::*; pub fn copy_dir<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> Result<(), io::Error> { let to = to.as_ref(); fs::create_dir(to)?; from.as_ref() .read_dir()? .par_bridge() .try_for_each(|dir_entry| -> io::Result<()> { let dir_entry = dir_entry?; let to = to.join(dir_entry.file_name()); if dir_entry.file_type()?.is_dir() { copy_dir(dir_entry.path(), to)?; } else { fs::copy(dir_entry.path(), to)?; } Ok(()) }) } 0707010000000F000081A40000000000000000000000016682237800000095000000000000000000000000000000000000002B00000000fuc-2.2.0/comparisons/cp_rayon/src/main.rsuse std::env; fn main() { let mut args = env::args_os().skip(1); cp_rayon::copy_dir(args.next().unwrap(), args.next().unwrap()).unwrap(); } 07070100000010000041ED0000000000000000000000026682237800000000000000000000000000000000000000000000002000000000fuc-2.2.0/comparisons/cp_stdlib07070100000011000081A40000000000000000000000016682237800000058000000000000000000000000000000000000002B00000000fuc-2.2.0/comparisons/cp_stdlib/Cargo.toml[package] name = "cp_stdlib" version = "0.0.0" edition.workspace = true publish = false 07070100000012000041ED0000000000000000000000026682237800000000000000000000000000000000000000000000002400000000fuc-2.2.0/comparisons/cp_stdlib/src07070100000013000081A4000000000000000000000001668223780000025B000000000000000000000000000000000000002B00000000fuc-2.2.0/comparisons/cp_stdlib/src/lib.rs#![allow(clippy::pedantic)] use std::{fs, io, path::Path}; pub fn copy_dir<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> Result<(), io::Error> { let to = to.as_ref(); fs::create_dir(to)?; from.as_ref() .read_dir()? .try_for_each(|dir_entry| -> io::Result<()> { let dir_entry = dir_entry?; let to = to.join(dir_entry.file_name()); if dir_entry.file_type()?.is_dir() { copy_dir(dir_entry.path(), to)?; } else { fs::copy(dir_entry.path(), to)?; } Ok(()) }) } 07070100000014000081A40000000000000000000000016682237800000096000000000000000000000000000000000000002C00000000fuc-2.2.0/comparisons/cp_stdlib/src/main.rsuse std::env; fn main() { let mut args = env::args_os().skip(1); cp_stdlib::copy_dir(args.next().unwrap(), args.next().unwrap()).unwrap(); } 07070100000015000041ED0000000000000000000000026682237800000000000000000000000000000000000000000000002300000000fuc-2.2.0/comparisons/rm_og_crappy07070100000016000081A400000000000000000000000166822378000000B0000000000000000000000000000000000000002E00000000fuc-2.2.0/comparisons/rm_og_crappy/Cargo.toml[package] name = "rm_og_crappy" version = "0.0.0" edition.workspace = true publish = false [dependencies] futures = "0.3.30" tokio = { version = "1.38.0", features = ["rt"] } 07070100000017000041ED0000000000000000000000026682237800000000000000000000000000000000000000000000002700000000fuc-2.2.0/comparisons/rm_og_crappy/src07070100000018000081A400000000000000000000000166822378000007FB000000000000000000000000000000000000002E00000000fuc-2.2.0/comparisons/rm_og_crappy/src/lib.rs#![allow(clippy::pedantic)] use std::{ fs, io, num::NonZeroUsize, path::{Path, PathBuf}, thread, }; use tokio::task::JoinHandle; /// Implementation of the OG post that started all this: /// <https://github.com/tokio-rs/tokio/issues/4172#issuecomment-945052350> pub fn remove_dir_all<P: AsRef<Path>>(path: P) -> Result<(), io::Error> { let parallelism = thread::available_parallelism().unwrap_or(unsafe { NonZeroUsize::new_unchecked(1) }); let runtime = tokio::runtime::Builder::new_current_thread() .max_blocking_threads(parallelism.get()) .build()?; runtime.block_on(fast_remove_dir_all(path.as_ref())) } async fn fast_remove_dir_all(path: &Path) -> io::Result<()> { let path = path.to_path_buf(); let path = tokio::task::spawn_blocking(|| -> io::Result<Option<PathBuf>> { let filetype = fs::symlink_metadata(&path)?.file_type(); if filetype.is_symlink() { fs::remove_file(&path)?; Ok(None) } else { Ok(Some(path)) } }) .await??; match path { None => Ok(()), Some(path) => remove_dir_all_recursive(path).await, } } async fn remove_dir_all_recursive(path: PathBuf) -> io::Result<()> { let path_copy = path.clone(); let tasks = tokio::task::spawn_blocking(move || -> io::Result<_> { let mut tasks = Vec::new(); for child in fs::read_dir(path)? { let child = child?; if child.file_type()?.is_dir() { tasks.push(spawn_remove_dir_all_recursive(&child.path())); } else { fs::remove_file(child.path())?; } } Ok(tasks) }) .await??; for result in futures::future::join_all(tasks).await { result??; } tokio::task::spawn_blocking(|| fs::remove_dir(path_copy)).await??; Ok(()) } fn spawn_remove_dir_all_recursive(path: &Path) -> JoinHandle<io::Result<()>> { tokio::task::spawn(remove_dir_all_recursive(path.to_path_buf())) } 07070100000019000081A40000000000000000000000016682237800000068000000000000000000000000000000000000002F00000000fuc-2.2.0/comparisons/rm_og_crappy/src/main.rsuse std::env; fn main() { rm_og_crappy::remove_dir_all(env::args_os().nth(1).unwrap()).unwrap(); } 0707010000001A000041ED0000000000000000000000026682237800000000000000000000000000000000000000000000001F00000000fuc-2.2.0/comparisons/rm_rayon0707010000001B000081A40000000000000000000000016682237800000078000000000000000000000000000000000000002A00000000fuc-2.2.0/comparisons/rm_rayon/Cargo.toml[package] name = "rm_rayon" version = "0.0.0" edition.workspace = true publish = false [dependencies] rayon = "1.10.0" 0707010000001C000041ED0000000000000000000000026682237800000000000000000000000000000000000000000000002300000000fuc-2.2.0/comparisons/rm_rayon/src0707010000001D000081A40000000000000000000000016682237800000230000000000000000000000000000000000000002A00000000fuc-2.2.0/comparisons/rm_rayon/src/lib.rs#![allow(clippy::pedantic)] use std::{fs, io, path::Path}; use rayon::prelude::*; pub fn remove_dir_all<P: AsRef<Path>>(path: P) -> Result<(), io::Error> { path.as_ref() .read_dir()? .par_bridge() .try_for_each(|dir_entry| -> io::Result<()> { let dir_entry = dir_entry?; if dir_entry.file_type()?.is_dir() { remove_dir_all(dir_entry.path())?; } else { fs::remove_file(dir_entry.path())?; } Ok(()) })?; fs::remove_dir(path) } 0707010000001E000081A40000000000000000000000016682237800000064000000000000000000000000000000000000002B00000000fuc-2.2.0/comparisons/rm_rayon/src/main.rsuse std::env; fn main() { rm_rayon::remove_dir_all(env::args_os().nth(1).unwrap()).unwrap(); } 0707010000001F000041ED0000000000000000000000026682237800000000000000000000000000000000000000000000002800000000fuc-2.2.0/comparisons/rm_remove_dir_all07070100000020000081A400000000000000000000000166822378000000B0000000000000000000000000000000000000003300000000fuc-2.2.0/comparisons/rm_remove_dir_all/Cargo.toml[package] name = "rm_remove_dir_all" version = "0.0.0" edition.workspace = true publish = false [dependencies] remove_dir_all = { version = "0.8.2", features = ["parallel"] } 07070100000021000041ED0000000000000000000000026682237800000000000000000000000000000000000000000000002C00000000fuc-2.2.0/comparisons/rm_remove_dir_all/src07070100000022000081A4000000000000000000000001668223780000006A000000000000000000000000000000000000003400000000fuc-2.2.0/comparisons/rm_remove_dir_all/src/main.rsuse std::env; fn main() { remove_dir_all::remove_dir_all(env::args_os().nth(1).unwrap()).unwrap(); } 07070100000023000041ED0000000000000000000000026682237800000000000000000000000000000000000000000000002000000000fuc-2.2.0/comparisons/rm_stdlib07070100000024000081A40000000000000000000000016682237800000058000000000000000000000000000000000000002B00000000fuc-2.2.0/comparisons/rm_stdlib/Cargo.toml[package] name = "rm_stdlib" version = "0.0.0" edition.workspace = true publish = false 07070100000025000041ED0000000000000000000000026682237800000000000000000000000000000000000000000000002400000000fuc-2.2.0/comparisons/rm_stdlib/src07070100000026000081A40000000000000000000000016682237800000064000000000000000000000000000000000000002C00000000fuc-2.2.0/comparisons/rm_stdlib/src/main.rsuse std::{env, fs}; fn main() { fs::remove_dir_all(env::args_os().nth(1).unwrap()).unwrap(); } 07070100000027000041ED0000000000000000000000026682237800000000000000000000000000000000000000000000001C00000000fuc-2.2.0/comparisons/tools07070100000028000081A40000000000000000000000016682237800000054000000000000000000000000000000000000002700000000fuc-2.2.0/comparisons/tools/Cargo.toml[package] name = "tools" version = "0.0.0" edition.workspace = true publish = false 07070100000029000041ED0000000000000000000000026682237800000000000000000000000000000000000000000000002000000000fuc-2.2.0/comparisons/tools/src0707010000002A000081A40000000000000000000000016682237800000597000000000000000000000000000000000000002800000000fuc-2.2.0/comparisons/tools/src/main.rsuse std::{env, fs, fs::File, io::Read}; const ORDER: &[&str] = &["1M_files", "100_000_files", "10_000_files", "10_files"]; fn main() { let mut v = Vec::new(); for entry in fs::read_dir(env::args_os().last().unwrap()).unwrap() { let entry = entry.unwrap(); if !entry.file_name().to_string_lossy().ends_with(".md") { continue; } v.push(entry); } v.sort_by(|a, b| { fn index(s: &str) -> usize { ORDER .iter() .enumerate() .find_map(|(i, target)| s.find(target).map(|_| i)) .unwrap() } let a = a.file_name(); let b = b.file_name(); let a = a.to_string_lossy(); let b = b.to_string_lossy(); index(&a).cmp(&index(&b)).then(a.cmp(&b)) }); for entry in v { println!("#### `{}`", entry.file_name().to_string_lossy()); let mut s = String::new(); File::open(entry.path()) .unwrap() .read_to_string(&mut s) .unwrap(); let s = s .replace( "`./target/release/rmz /tmp/ftzz`", "*`./target/release/rmz /tmp/ftzz`*", ) .replace( "`./target/release/cpz /tmp/ftzz /tmp/ftzzz`", "*`./target/release/cpz /tmp/ftzz /tmp/ftzzz`*", ); println!("{s}"); } } 0707010000002B000041ED0000000000000000000000026682237800000000000000000000000000000000000000000000000E00000000fuc-2.2.0/cpz0707010000002C000081A40000000000000000000000016682237800000632000000000000000000000000000000000000001900000000fuc-2.2.0/cpz/Cargo.toml[package] name = "cpz" version.workspace = true authors.workspace = true edition.workspace = true description = "Fast cp provides an alternative to cp that focuses on maximizing performance." repository.workspace = true keywords = ["tools", "files", "cp"] categories = ["command-line-utilities", "development-tools", "filesystem"] license.workspace = true rust-version.workspace = true [dependencies] clap = { version = "4.5.8", features = ["derive", "wrap_help"] } error-stack = "0.4.1" fuc_engine = { version = "2", path = "../fuc_engine" } once_cell = "1.18.0" indicatif = { version = "0.17.8", optional = true } thiserror = "1.0.61" tracing = { version = "0.1.40", optional = true } tracing-indicatif = { version = "0.3.6", optional = true } tracing-subscriber = { version = "0.3.18", features = ["env-filter"], optional = true } tracing-tracy = { version = "0.11.0", features = ["flush-on-exit"], optional = true } tracy-client = { version = "0.17.0", optional = true } [dev-dependencies] cache-size = "0.7.0" criterion = "0.5.1" memmap2 = "0.9.4" rand = "0.8.5" supercilex-tests = { version = "0.4.6", default-features = false, features = ["clap"] } tempfile = "3.10.1" trycmd = "0.15.4" [target.'cfg(unix)'.dev-dependencies] rustix = { version = "0.38.34", features = ["fs", "pipe"] } [features] trace = ["fuc_engine/tracing", "dep:tracing", "dep:tracing-subscriber", "dep:tracing-tracy", "dep:tracy-client"] progress = ["fuc_engine/tracing", "dep:tracing", "dep:tracing-subscriber", "dep:tracing-indicatif", "dep:indicatif"] [[bench]] name = "copy_methods" harness = false 0707010000002D000041ED0000000000000000000000026682237800000000000000000000000000000000000000000000001800000000fuc-2.2.0/cpz/README.in0707010000002E000081A40000000000000000000000016682237800000000000000000000000000000000000000000000002100000000fuc-2.2.0/cpz/README.in/existing0707010000002F000081A40000000000000000000000016682237800000000000000000000000000000000000000000000001D00000000fuc-2.2.0/cpz/README.in/from07070100000030000041ED0000000000000000000000026682237800000000000000000000000000000000000000000000002100000000fuc-2.2.0/cpz/README.in/from_dir07070100000031000081A40000000000000000000000016682237800000000000000000000000000000000000000000000002500000000fuc-2.2.0/cpz/README.in/from_dir/bar07070100000032000081A400000000000000000000000166822378000007AA000000000000000000000000000000000000001800000000fuc-2.2.0/cpz/README.md# cp zippy [![Crates.io](https://img.shields.io/crates/v/cpz)](https://crates.io/crates/cpz) A zippy alternative to `cp`, a tool to copy files and directories. ## Installation ### Use prebuilt binaries Binaries for a number of platforms are available on the [release page](https://github.com/SUPERCILEX/fuc/releases/latest). ### Build from source ```console,ignore $ cargo install cpz ``` > To install cargo, follow > [these instructions](https://doc.rust-lang.org/cargo/getting-started/installation.html). ## Usage Background: https://github.com/SUPERCILEX/fuc/blob/master/README.md Copy a file: ```console $ cpz from to ``` Copy a directory: ```console $ cpz from_dir to_dir ``` Overwrite existing files: ```console $ cpz -f from existing ``` Flip the argument order (for better composability with other commands for example): ```console $ cpz -t to_first from ``` Force the source files to be copied into the destination by making the path look like a directory: ```console,ignore $ cpz from dest/ ``` More details: ```console $ cpz --help A zippy alternative to `cp`, a tool to copy files and directories Usage: cpz[EXE] [OPTIONS] <FROM>... <TO> Arguments: <FROM>... The file(s) or directory(ies) to be copied If multiple files are specified, they will be copied into the target destination rather than to it. The same is true of directory names (`foo/`, `.`, `..`): that is, `cpz a b/` places `a` inside `b` as opposed to `cpz a b` which makes `b` become `a`. <TO> The copy destination Options: -f, --force Overwrite existing files -t, --reverse-args Reverse the argument order so that it becomes `cpz <TO> <FROM>...` -L, --dereference Follow symlinks in the files to be copied rather than copying the symlinks themselves -h, --help Print help (use `-h` for a summary) -V, --version Print version ``` 07070100000033000041ED0000000000000000000000026682237800000000000000000000000000000000000000000000001900000000fuc-2.2.0/cpz/README.out07070100000034000081A40000000000000000000000016682237800000000000000000000000000000000000000000000001F00000000fuc-2.2.0/cpz/README.out/.keep07070100000035000041ED0000000000000000000000026682237800000000000000000000000000000000000000000000001600000000fuc-2.2.0/cpz/benches07070100000036000081A40000000000000000000000016682237800004A50000000000000000000000000000000000000002600000000fuc-2.2.0/cpz/benches/copy_methods.rsuse std::{ alloc, alloc::Layout, fs::{copy, File, OpenOptions}, io::{BufRead, BufReader, Read, Write}, path::{Path, PathBuf}, time::Duration, }; use cache_size::l1_cache_size; use criterion::{ criterion_group, criterion_main, measurement::WallTime, AxisScale, BatchSize, BenchmarkGroup, BenchmarkId, Criterion, PlotConfiguration, Throughput, }; use memmap2::{Mmap, MmapOptions}; use rand::{thread_rng, RngCore}; use tempfile::{tempdir, TempDir}; // Don't use an OS backed tempfile since it might change the performance // characteristics of our copy struct NormalTempFile { dir: TempDir, from: PathBuf, to: PathBuf, } impl NormalTempFile { fn create(bytes: u64, direct_io: bool) -> Self { let dir = tempdir().unwrap(); let from = dir.path().join("from"); let buf = create_random_buffer(usize::try_from(bytes).unwrap(), direct_io); open_standard( &from, #[cfg(target_os = "linux")] direct_io, ) .write_all(&buf) .unwrap(); Self { to: dir.path().join("to"), dir, from, } } } /// Doesn't use direct I/O, so files will be mem cached fn with_memcache(c: &mut Criterion) { let mut group = c.benchmark_group("with_memcache"); group.plot_config(PlotConfiguration::default().summary_scale(AxisScale::Logarithmic)); for num_bytes in [1 << 12, 1 << 20, 1 << 25] { group.throughput(Throughput::Bytes(num_bytes)); add_benches(&mut group, num_bytes, false); } } /// Use direct I/O to create the file to be copied so it's not cached initially fn initially_uncached(c: &mut Criterion) { let mut group = c.benchmark_group("initially_uncached"); group.plot_config(PlotConfiguration::default().summary_scale(AxisScale::Logarithmic)); for num_bytes in [1 << 12, 1 << 20, 1 << 25] { group.throughput(Throughput::Bytes(num_bytes)); add_benches(&mut group, num_bytes, true); } } fn empty_files(c: &mut Criterion) { let mut group = c.benchmark_group("empty_files"); group.throughput(Throughput::Elements(1)); group.bench_function("fs::copy", |b| { b.iter_batched( || NormalTempFile::create(0, false), |files| { copy(files.from, files.to).unwrap(); files.dir }, BatchSize::LargeInput, ); }); group.bench_function("File::create", |b| { b.iter_batched( || NormalTempFile::create(0, false), |files| { File::create(files.to).unwrap(); files.dir }, BatchSize::LargeInput, ); }); #[cfg(target_os = "linux")] group.bench_function("mknod", |b| { b.iter_batched( || NormalTempFile::create(0, false), |files| { use rustix::fs::{mknodat, FileType, Mode, CWD}; mknodat( CWD, files.to.as_path(), FileType::RegularFile, Mode::empty(), 0, ) .unwrap(); files.dir }, BatchSize::LargeInput, ); }); #[cfg(target_os = "linux")] group.bench_function("mknodat", |b| { b.iter_batched( || { let files = NormalTempFile::create(0, false); let dir = File::open(files.dir.path()).unwrap(); (files, dir) }, |(files, dir)| { use rustix::fs::{mknodat, FileType, Mode}; mknodat( &dir, files.to.file_name().unwrap(), FileType::RegularFile, Mode::empty(), 0, ) .unwrap(); files.dir }, BatchSize::LargeInput, ); }); #[cfg(unix)] group.bench_function("open", |b| { let files = NormalTempFile::create(0, false); let path = files.to.as_path(); b.iter(|| { use rustix::fs::{openat, Mode, OFlags, CWD}; openat(CWD, path, OFlags::CREATE, Mode::RWXU).unwrap() }); }); #[cfg(unix)] group.bench_function("openat", |b| { let files = NormalTempFile::create(0, false); let dir = File::open(files.dir.path()).unwrap(); let path = files.to.file_name().unwrap(); b.iter(|| { use rustix::fs::{openat, Mode, OFlags}; openat(&dir, path, OFlags::CREATE, Mode::RWXU).unwrap() }); }); } fn just_writes(c: &mut Criterion) { let mut group = c.benchmark_group("just_writes"); group.plot_config(PlotConfiguration::default().summary_scale(AxisScale::Logarithmic)); for num_bytes in [1 << 12, 1 << 20, 1 << 25] { group.throughput(Throughput::Bytes(num_bytes)); group.bench_with_input( BenchmarkId::new("open_memcache", num_bytes), &num_bytes, |b, &num_files| { b.iter_batched( || { let dir = tempdir().unwrap(); let buf = create_random_buffer(usize::try_from(num_files).unwrap(), false); (dir, buf) }, |(dir, buf)| { File::create(dir.path().join("file")) .unwrap() .write_all(&buf) .unwrap(); (dir, buf) }, BatchSize::PerIteration, ); }, ); group.bench_with_input( BenchmarkId::new("open_nocache", num_bytes), &num_bytes, |b, &num_files| { b.iter_batched( || { let dir = tempdir().unwrap(); let buf = create_random_buffer(usize::try_from(num_files).unwrap(), true); (dir, buf) }, |(dir, buf)| { let mut out = open_standard( dir.path().join("file").as_ref(), #[cfg(target_os = "linux")] true, ); out.set_len(num_files).unwrap(); out.write_all(&buf).unwrap(); (dir, buf) }, BatchSize::PerIteration, ); }, ); } } #[allow(clippy::too_many_lines)] fn add_benches(group: &mut BenchmarkGroup<WallTime>, num_bytes: u64, direct_io: bool) { group.bench_with_input( BenchmarkId::new("fs::copy", num_bytes), &num_bytes, |b, &num_files| { b.iter_batched( || NormalTempFile::create(num_files, direct_io), |files| { copy(files.from, files.to).unwrap(); files.dir }, BatchSize::PerIteration, ); }, ); #[cfg(target_os = "linux")] group.bench_with_input( BenchmarkId::new("copy_file_range", num_bytes), &num_bytes, |b, &num_files| { use rustix::fs::copy_file_range; b.iter_batched( || NormalTempFile::create(num_files, direct_io), |files| { let from = File::open(files.from).unwrap(); let to = OpenOptions::new() .read(true) .write(true) .create(true) .truncate(false) .open(files.to) .unwrap(); to.set_len(num_files).unwrap(); let mut bytes_remaining = usize::try_from(num_files).unwrap(); while bytes_remaining > 0 { bytes_remaining -= copy_file_range(&from, None, &to, None, bytes_remaining).unwrap(); } files.dir }, BatchSize::PerIteration, ); }, ); group.bench_with_input( BenchmarkId::new("buffered", num_bytes), &num_bytes, |b, &num_files| { b.iter_batched( || NormalTempFile::create(num_files, direct_io), |files| { let reader = BufReader::new(File::open(files.from).unwrap()); write_from_buffer(files.to, reader); files.dir }, BatchSize::PerIteration, ); }, ); group.bench_with_input( BenchmarkId::new("buffered_l1_tuned", num_bytes), &num_bytes, |b, &num_files| { b.iter_batched( || NormalTempFile::create(num_files, direct_io), |files| { let l1_cache_size = l1_cache_size().unwrap(); let reader = BufReader::with_capacity(l1_cache_size, File::open(files.from).unwrap()); write_from_buffer(files.to, reader); files.dir }, BatchSize::PerIteration, ); }, ); group.bench_with_input( BenchmarkId::new("buffered_readahead_tuned", num_bytes), &num_bytes, |b, &num_files| { b.iter_batched( || NormalTempFile::create(num_files, direct_io), |files| { let readahead_size = 1 << 17; // See https://eklitzke.org/efficient-file-copying-on-linux let reader = BufReader::with_capacity(readahead_size, File::open(files.from).unwrap()); write_from_buffer(files.to, reader); files.dir }, BatchSize::PerIteration, ); }, ); #[cfg(unix)] group.bench_with_input( BenchmarkId::new("buffered_parallel", num_bytes), &num_bytes, |b, &num_files| { b.iter_batched( || NormalTempFile::create(num_files, direct_io), |files| { use std::{os::unix::fs::FileExt, thread}; let threads = u64::try_from(thread::available_parallelism().unwrap().get()).unwrap(); let chunk_size = num_bytes / threads; let from = File::open(files.from).unwrap(); let to = File::create(files.to).unwrap(); to.set_len(num_files).unwrap(); let mut results = Vec::with_capacity(usize::try_from(threads).unwrap()); for i in 0..threads { let from = from.try_clone().unwrap(); let to = to.try_clone().unwrap(); results.push(thread::spawn(move || { let mut buf = Vec::with_capacity(usize::try_from(chunk_size).unwrap()); // We write those bytes immediately after and dropping u8s does nothing #[allow(clippy::uninit_vec)] unsafe { buf.set_len(usize::try_from(chunk_size).unwrap()); } from.read_exact_at(&mut buf, i * chunk_size).unwrap(); to.write_all_at(&buf, i * chunk_size).unwrap(); })); } for handle in results { handle.join().unwrap(); } files.dir }, BatchSize::PerIteration, ); }, ); group.bench_with_input( BenchmarkId::new("buffered_entire_file", num_bytes), &num_bytes, |b, &num_files| { b.iter_batched( || NormalTempFile::create(num_files, direct_io), |files| { let mut from = File::open(files.from).unwrap(); let mut to = File::create(files.to).unwrap(); to.set_len(num_files).unwrap(); let mut buf = Vec::with_capacity(usize::try_from(num_files).unwrap()); from.read_to_end(&mut buf).unwrap(); to.write_all(&buf).unwrap(); files.dir }, BatchSize::PerIteration, ); }, ); group.bench_with_input( BenchmarkId::new("mmap_read_only", num_bytes), &num_bytes, |b, &num_files| { b.iter_batched( || NormalTempFile::create(num_files, direct_io), |files| { let from = File::open(files.from).unwrap(); let reader = unsafe { Mmap::map(&from) }.unwrap(); let mut to = File::create(files.to).unwrap(); to.write_all(reader.as_ref()).unwrap(); files.dir }, BatchSize::PerIteration, ); }, ); group.bench_with_input( BenchmarkId::new("mmap_read_only_truncate", num_bytes), &num_bytes, |b, &num_files| { b.iter_batched( || NormalTempFile::create(num_files, direct_io), |files| { let from = File::open(files.from).unwrap(); let reader = unsafe { Mmap::map(&from) }.unwrap(); let mut to = File::create(files.to).unwrap(); to.set_len(num_files).unwrap(); to.write_all(reader.as_ref()).unwrap(); files.dir }, BatchSize::PerIteration, ); }, ); #[cfg(target_os = "linux")] group.bench_with_input( BenchmarkId::new("mmap_read_only_fallocate", num_bytes), &num_bytes, |b, &num_files| { b.iter_batched( || NormalTempFile::create(num_files, direct_io), |files| { let from = File::open(files.from).unwrap(); let reader = unsafe { Mmap::map(&from) }.unwrap(); let mut to = File::create(files.to).unwrap(); allocate(&to, num_files); to.write_all(reader.as_ref()).unwrap(); files.dir }, BatchSize::PerIteration, ); }, ); group.bench_with_input( BenchmarkId::new("mmap_rw_truncate", num_bytes), &num_bytes, |b, &num_files| { b.iter_batched( || NormalTempFile::create(num_files, direct_io), |files| { let from = File::open(files.from).unwrap(); let to = OpenOptions::new() .read(true) .write(true) .create(true) .truncate(false) .open(files.to) .unwrap(); to.set_len(num_files).unwrap(); let reader = unsafe { Mmap::map(&from) }.unwrap(); let mut writer = unsafe { MmapOptions::new().map_mut(&to) }.unwrap(); writer.copy_from_slice(reader.as_ref()); files.dir }, BatchSize::PerIteration, ); }, ); #[cfg(target_os = "linux")] group.bench_with_input( BenchmarkId::new("sendfile", num_bytes), &num_bytes, |b, &num_files| { use rustix::fs::sendfile; b.iter_batched( || NormalTempFile::create(num_files, direct_io), |files| { let from = File::open(files.from).unwrap(); let to = OpenOptions::new() .read(true) .write(true) .create(true) .truncate(false) .open(files.to) .unwrap(); to.set_len(num_files).unwrap(); let mut bytes_remaining = usize::try_from(num_files).unwrap(); while bytes_remaining > 0 { bytes_remaining -= sendfile(&to, &from, None, bytes_remaining).unwrap(); } files.dir }, BatchSize::PerIteration, ); }, ); } fn open_standard(path: &Path, #[cfg(target_os = "linux")] direct_io: bool) -> File { let mut options = OpenOptions::new(); options.write(true).create(true).truncate(true); #[cfg(target_os = "linux")] if direct_io { use std::os::unix::fs::OpenOptionsExt; options.custom_flags(i32::try_from(rustix::pipe::PipeFlags::DIRECT.bits()).unwrap()); } options.open(path).unwrap() } fn write_from_buffer(to: PathBuf, mut reader: BufReader<File>) { let mut to = File::create(to).unwrap(); to.set_len(reader.get_ref().metadata().unwrap().len()) .unwrap(); loop { let len = { let buf = reader.fill_buf().unwrap(); if buf.is_empty() { break; } to.write_all(buf).unwrap(); buf.len() }; reader.consume(len); } } #[cfg(target_os = "linux")] fn allocate(file: &File, len: u64) { use rustix::fs::{fallocate, FallocateFlags}; fallocate(file, FallocateFlags::empty(), 0, len).unwrap(); } fn create_random_buffer(bytes: usize, direct_io: bool) -> Vec<u8> { assert!( !direct_io || bytes % (1 << 12) == 0, "Num bytes ({bytes}) must be divisible by 2^12", ); let mut buf = if direct_io { let layout = Layout::from_size_align(bytes, 1 << 12).unwrap(); let ptr = unsafe { alloc::alloc(layout) }; unsafe { Vec::<u8>::from_raw_parts(ptr, bytes, bytes) } } else { let mut v = Vec::with_capacity(bytes); // We write those bytes immediately after and dropping u8s does nothing #[allow(clippy::uninit_vec)] unsafe { v.set_len(bytes); } v }; thread_rng().fill_bytes(buf.as_mut_slice()); buf } criterion_group! { name = benches; config = Criterion::default().noise_threshold(0.02).warm_up_time(Duration::from_secs(1)); targets = with_memcache, initially_uncached, empty_files, just_writes, } criterion_main!(benches); 07070100000037000081A40000000000000000000000016682237800000245000000000000000000000000000000000000002D00000000fuc-2.2.0/cpz/command-reference-short.goldenA zippy alternative to `cp`, a tool to copy files and directories Usage: cpz [OPTIONS] <FROM>... <TO> Arguments: <FROM>... The file(s) or directory(ies) to be copied <TO> The copy destination Options: -f, --force Overwrite existing files -t, --reverse-args Reverse the argument order so that it becomes `cpz <TO> <FROM>...` -L, --dereference Follow symlinks in the files to be copied rather than copying the symlinks themselves -h, --help Print help (use `--help` for more detail) -V, --version Print version 07070100000038000081A40000000000000000000000016682237800000377000000000000000000000000000000000000002700000000fuc-2.2.0/cpz/command-reference.goldenA zippy alternative to `cp`, a tool to copy files and directories Usage: cpz [OPTIONS] <FROM>... <TO> Arguments: <FROM>... The file(s) or directory(ies) to be copied If multiple files are specified, they will be copied into the target destination rather than to it. The same is true of directory names (`foo/`, `.`, `..`): that is, `cpz a b/` places `a` inside `b` as opposed to `cpz a b` which makes `b` become `a`. <TO> The copy destination Options: -f, --force Overwrite existing files -t, --reverse-args Reverse the argument order so that it becomes `cpz <TO> <FROM>...` -L, --dereference Follow symlinks in the files to be copied rather than copying the symlinks themselves -h, --help Print help (use `-h` for a summary) -V, --version Print version 07070100000039000041ED0000000000000000000000026682237800000000000000000000000000000000000000000000001200000000fuc-2.2.0/cpz/src0707010000003A000081A4000000000000000000000001668223780000247E000000000000000000000000000000000000001A00000000fuc-2.2.0/cpz/src/main.rsuse std::{ fs, mem::swap, path::{PathBuf, MAIN_SEPARATOR, MAIN_SEPARATOR_STR}, }; use clap::{ArgAction, Parser, ValueHint}; use error_stack::Report; use fuc_engine::{CopyOp, Error}; use once_cell::sync::Lazy as LazyCell; /// A zippy alternative to `cp`, a tool to copy files and directories #[derive(Parser, Debug)] #[command(version, author = "Alex Saveau (@SUPERCILEX)")] #[command(infer_subcommands = true, infer_long_args = true)] #[command(disable_help_flag = true)] #[command(arg_required_else_help = true)] #[command(max_term_width = 100)] #[cfg_attr(test, command(help_expected = true))] struct Cpz { /// The file(s) or directory(ies) to be copied /// /// If multiple files are specified, they will be copied into the target /// destination rather than to it. The same is true of directory names /// (`foo/`, `.`, `..`): that is, `cpz a b/` places `a` inside `b` as /// opposed to `cpz a b` which makes `b` become `a`. #[arg(required = true)] #[arg(value_hint = ValueHint::AnyPath)] from: Vec<PathBuf>, /// The copy destination #[arg(required = true)] #[arg(value_hint = ValueHint::AnyPath)] to: PathBuf, /// Overwrite existing files #[arg(short, long, default_value_t = false)] force: bool, /// Reverse the argument order so that it becomes `cpz <TO> <FROM>...` #[arg(short = 't', long, default_value_t = false)] reverse_args: bool, /// Follow symlinks in the files to be copied rather than copying the /// symlinks themselves #[arg(short = 'L', long, default_value_t = false)] #[arg(aliases = ["follow-symlinks"])] dereference: bool, #[arg(short, long, short_alias = '?', global = true)] #[arg(action = ArgAction::Help, help = "Print help (use `--help` for more detail)")] #[arg(long_help = "Print help (use `-h` for a summary)")] help: Option<bool>, } #[derive(thiserror::Error, Debug)] enum CliError { #[error("{0}")] Wrapper(String), } #[cfg(feature = "trace")] #[global_allocator] static GLOBAL: tracy_client::ProfiledAllocator<std::alloc::System> = tracy_client::ProfiledAllocator::new(std::alloc::System, 100); #[cfg(feature = "trace")] fn init_trace() { use tracing_subscriber::{ filter::LevelFilter, fmt::format::DefaultFields, layer::SubscriberExt, util::SubscriberInitExt, EnvFilter, }; #[derive(Default)] struct Config(DefaultFields); impl tracing_tracy::Config for Config { type Formatter = DefaultFields; fn formatter(&self) -> &Self::Formatter { &self.0 } fn stack_depth(&self, _: &tracing::Metadata<'_>) -> u16 { 32 } fn format_fields_in_zone_name(&self) -> bool { false } } tracing_subscriber::registry() .with(tracing_tracy::TracyLayer::new(Config::default())) .with( EnvFilter::builder() .with_default_directive(LevelFilter::TRACE.into()) .from_env_lossy(), ) .init(); } #[cfg(feature = "progress")] fn init_progress() { use std::time::Duration; use indicatif::{ProgressState, ProgressStyle}; use tracing::level_filters::LevelFilter; use tracing_indicatif::IndicatifLayer; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, EnvFilter}; let indicatif_layer = IndicatifLayer::new() .with_progress_style( ProgressStyle::with_template( "{color_start}{span_child_prefix}{span_fields} -- {span_name} {wide_msg} \ {elapsed_subsec}{color_end}", ) .unwrap() .with_key( "elapsed_subsec", |state: &ProgressState, writer: &mut dyn std::fmt::Write| { let seconds = state.elapsed().as_secs(); let sub_seconds = (state.elapsed().as_millis() % 1000) / 100; let _ = write!(writer, "{}.{}s", seconds, sub_seconds); }, ) .with_key( "color_start", |state: &ProgressState, writer: &mut dyn std::fmt::Write| { let elapsed = state.elapsed(); if elapsed > Duration::from_secs(8) { // Red let _ = write!(writer, "\x1b[{}m", 1 + 30); } else if elapsed > Duration::from_secs(4) { // Yellow let _ = write!(writer, "\x1b[{}m", 3 + 30); } }, ) .with_key( "color_end", |state: &ProgressState, writer: &mut dyn std::fmt::Write| { if state.elapsed() > Duration::from_secs(4) { let _ = write!(writer, "\x1b[0m"); } }, ), ) .with_span_child_prefix_symbol("↳ ") .with_span_child_prefix_indent(" "); tracing_subscriber::registry() .with(tracing_subscriber::fmt::layer().with_writer(indicatif_layer.get_stderr_writer())) .with(indicatif_layer) .with( EnvFilter::builder() .with_default_directive(LevelFilter::INFO.into()) .from_env_lossy(), ) .init(); } fn main() -> error_stack::Result<(), CliError> { #[cfg(not(debug_assertions))] error_stack::Report::install_debug_hook::<std::panic::Location>(|_, _| {}); #[cfg(feature = "trace")] init_trace(); #[cfg(feature = "progress")] init_progress(); let args = Cpz::parse(); copy(args).map_err(|e| { let wrapper = CliError::Wrapper(format!("{e}")); match e { Error::Io { error, context } => Report::from(error) .attach_printable(context) .change_context(wrapper), Error::AlreadyExists { file } => { let report = Report::from(wrapper); match file.symlink_metadata().map(|m| m.is_dir()) { Ok(true) => { let mut file = file.into_os_string(); file.push(MAIN_SEPARATOR_STR); report .attach_printable(format!( "Use the path {file:?} to copy into the directory." )) .attach_printable( "Use --force to merge directories (overwriting existing files).", ) } Ok(false) | Err(_) => report.attach_printable("Use --force to overwrite."), } } Error::Join | Error::BadPath | Error::Internal => Report::from(wrapper), Error::PreserveRoot | Error::NotFound { file: _ } => unreachable!(), } }) } fn copy( Cpz { mut from, mut to, force, reverse_args, dereference, help: _, }: Cpz, ) -> Result<(), Error> { if reverse_args { swap(&mut to, &mut from[0]); } let from = from; let to = to; #[allow(clippy::unnested_or_patterns)] let is_into_directory = LazyCell::new(|| { matches!( { let path_str = to.to_string_lossy(); let mut chars = path_str.chars(); (chars.next_back(), chars.next_back(), chars.next_back()) }, (Some(MAIN_SEPARATOR), _, _) // */ | (Some('.'), None, _) // . | (Some('.'), Some(MAIN_SEPARATOR), _) // */. | (Some('.'), Some('.'), None) // .. | (Some('.'), Some('.'), Some(MAIN_SEPARATOR)) // */.. ) }); if from.len() > 1 || *is_into_directory { fs::create_dir_all(&to).map_err(|error| Error::Io { error, context: format!("Failed to create directory {to:?}").into(), })?; } if from.len() > 1 { CopyOp::builder() .files(from.into_iter().map(|path| { let to = path .file_name() .map_or_else(|| to.clone(), |name| to.join(name)); (path, to) })) .force(force) .follow_symlinks(dereference) .build() .run() } else { CopyOp::builder() .files([{ let from = from.into_iter().next().unwrap(); let to = { let is_into_directory = *is_into_directory; let mut to = to; if is_into_directory { if let Some(name) = from.file_name() { to.push(name); } } to }; (from, to) }]) .force(force) .follow_symlinks(dereference) .build() .run() } } #[cfg(test)] mod cli_tests { use clap::CommandFactory; use super::*; #[test] fn verify_app() { Cpz::command().debug_assert(); } #[test] fn help_for_review() { supercilex_tests::help_for_review(Cpz::command()); } } 0707010000003B000041ED0000000000000000000000026682237800000000000000000000000000000000000000000000001400000000fuc-2.2.0/cpz/tests0707010000003C000081A40000000000000000000000016682237800000048000000000000000000000000000000000000001B00000000fuc-2.2.0/cpz/tests/api.rs#[test] fn readme() { trycmd::TestCases::new().case("README.md"); } 0707010000003D000041ED0000000000000000000000026682237800000000000000000000000000000000000000000000001500000000fuc-2.2.0/fuc_engine0707010000003E000081A40000000000000000000000016682237800000416000000000000000000000000000000000000002000000000fuc-2.2.0/fuc_engine/Cargo.toml[package] name = "fuc_engine" version.workspace = true authors.workspace = true edition.workspace = true description = "Engine for the FUCing project." repository.workspace = true keywords = ["tools", "files"] categories = ["filesystem"] license.workspace = true rust-version.workspace = true [dependencies] crossbeam-channel = "0.5.13" once_cell = "1.18.0" thiserror = "1.0.61" tracing = { version = "0.1.40", default-features = false, features = ["attributes"], optional = true } typed-builder = "0.18.2" [target.'cfg(target_os = "linux")'.dependencies] rustix = { version = "0.38.34", features = ["fs", "thread", "linux_latest"] } [target.'cfg(not(target_os = "linux"))'.dependencies] rayon = "1.10.0" [target.'cfg(target_os = "windows")'.dependencies] remove_dir_all = { version = "0.8.2", features = ["parallel"] } [dev-dependencies] ftzz = "3.0.0" io-adapters = "0.3.0" rstest = { version = "0.21.0", default-features = false } supercilex-tests = { version = "0.4.6", default-features = false, features = ["api"] } tempfile = "3.10.1" 0707010000003F000081A400000000000000000000000166822378000000ED000000000000000000000000000000000000001F00000000fuc-2.2.0/fuc_engine/README.md# The FUC-ing engine [![Crates.io](https://img.shields.io/crates/v/fuc-engine)](https://crates.io/crates/fuc-engine) The scheduling engine that powers FUCs. It can be used as a library, but is not currently designed for that use case. 07070100000040000081A40000000000000000000000016682237800002959000000000000000000000000000000000000002000000000fuc-2.2.0/fuc_engine/api.goldenpub mod fuc_engine pub enum fuc_engine::Error pub fuc_engine::Error::AlreadyExists pub fuc_engine::Error::AlreadyExists::file: std::path::PathBuf pub fuc_engine::Error::BadPath pub fuc_engine::Error::Internal pub fuc_engine::Error::Io pub fuc_engine::Error::Io::context: alloc::borrow::Cow<'static, str> pub fuc_engine::Error::Io::error: std::io::error::Error pub fuc_engine::Error::Join pub fuc_engine::Error::NotFound pub fuc_engine::Error::NotFound::file: std::path::PathBuf pub fuc_engine::Error::PreserveRoot impl core::error::Error for fuc_engine::Error impl core::fmt::Debug for fuc_engine::Error pub fn fuc_engine::Error::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result impl core::fmt::Display for fuc_engine::Error pub fn fuc_engine::Error::fmt(&self, __formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result impl core::marker::Freeze for fuc_engine::Error impl core::marker::Send for fuc_engine::Error impl core::marker::Sync for fuc_engine::Error impl core::marker::Unpin for fuc_engine::Error impl !core::panic::unwind_safe::RefUnwindSafe for fuc_engine::Error impl !core::panic::unwind_safe::UnwindSafe for fuc_engine::Error impl<T, U> core::convert::Into<U> for fuc_engine::Error where U: core::convert::From<T> pub fn fuc_engine::Error::into(self) -> U impl<T, U> core::convert::TryFrom<U> for fuc_engine::Error where U: core::convert::Into<T> pub type fuc_engine::Error::Error = core::convert::Infallible pub fn fuc_engine::Error::try_from(value: U) -> core::result::Result<T, <T as core::convert::TryFrom<U>>::Error> impl<T, U> core::convert::TryInto<U> for fuc_engine::Error where U: core::convert::TryFrom<T> pub type fuc_engine::Error::Error = <U as core::convert::TryFrom<T>>::Error pub fn fuc_engine::Error::try_into(self) -> core::result::Result<U, <U as core::convert::TryFrom<T>>::Error> impl<T> alloc::string::ToString for fuc_engine::Error where T: core::fmt::Display + core::marker::Sized pub fn fuc_engine::Error::to_string(&self) -> alloc::string::String impl<T> core::any::Any for fuc_engine::Error where T: 'static + core::marker::Sized pub fn fuc_engine::Error::type_id(&self) -> core::any::TypeId impl<T> core::borrow::Borrow<T> for fuc_engine::Error where T: core::marker::Sized pub fn fuc_engine::Error::borrow(&self) -> &T impl<T> core::borrow::BorrowMut<T> for fuc_engine::Error where T: core::marker::Sized pub fn fuc_engine::Error::borrow_mut(&mut self) -> &mut T impl<T> core::convert::From<T> for fuc_engine::Error pub fn fuc_engine::Error::from(t: T) -> T impl<T> tracing::instrument::Instrument for fuc_engine::Error impl<T> tracing::instrument::WithSubscriber for fuc_engine::Error pub struct fuc_engine::CopyOp<'a, 'b, I1: core::convert::Into<alloc::borrow::Cow<'a, std::path::Path>> + 'a, I2: core::convert::Into<alloc::borrow::Cow<'b, std::path::Path>> + 'b, F: core::iter::traits::collect::IntoIterator<Item = (I1, I2)>> impl<'a, 'b, I1: core::convert::Into<alloc::borrow::Cow<'a, std::path::Path>> + 'a, I2: core::convert::Into<alloc::borrow::Cow<'b, std::path::Path>> + 'b, F: core::iter::traits::collect::IntoIterator<Item = (I1, I2)>> fuc_engine::CopyOp<'a, 'b, I1, I2, F> pub fn fuc_engine::CopyOp<'a, 'b, I1, I2, F>::run(self) -> core::result::Result<(), fuc_engine::Error> impl<'a, 'b, I1: core::convert::Into<alloc::borrow::Cow<'a, std::path::Path>> + 'a, I2: core::convert::Into<alloc::borrow::Cow<'b, std::path::Path>> + 'b, F: core::iter::traits::collect::IntoIterator<Item = (I1, I2)>> fuc_engine::CopyOp<'a, 'b, I1, I2, F> pub fn fuc_engine::CopyOp<'a, 'b, I1, I2, F>::builder() -> CopyOpBuilder<'a, 'b, I1, I2, F, ((), (), (), (), ())> impl<'a, 'b, I1: core::fmt::Debug + core::convert::Into<alloc::borrow::Cow<'a, std::path::Path>> + 'a, I2: core::fmt::Debug + core::convert::Into<alloc::borrow::Cow<'b, std::path::Path>> + 'b, F: core::fmt::Debug + core::iter::traits::collect::IntoIterator<Item = (I1, I2)>> core::fmt::Debug for fuc_engine::CopyOp<'a, 'b, I1, I2, F> pub fn fuc_engine::CopyOp<'a, 'b, I1, I2, F>::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result impl<'a, 'b, I1, I2, F> core::marker::Freeze for fuc_engine::CopyOp<'a, 'b, I1, I2, F> where F: core::marker::Freeze impl<'a, 'b, I1, I2, F> core::marker::Send for fuc_engine::CopyOp<'a, 'b, I1, I2, F> where F: core::marker::Send, I1: core::marker::Sync, I2: core::marker::Sync impl<'a, 'b, I1, I2, F> core::marker::Sync for fuc_engine::CopyOp<'a, 'b, I1, I2, F> where F: core::marker::Sync, I1: core::marker::Sync, I2: core::marker::Sync impl<'a, 'b, I1, I2, F> core::marker::Unpin for fuc_engine::CopyOp<'a, 'b, I1, I2, F> where F: core::marker::Unpin impl<'a, 'b, I1, I2, F> core::panic::unwind_safe::RefUnwindSafe for fuc_engine::CopyOp<'a, 'b, I1, I2, F> where F: core::panic::unwind_safe::RefUnwindSafe, I1: core::panic::unwind_safe::RefUnwindSafe, I2: core::panic::unwind_safe::RefUnwindSafe impl<'a, 'b, I1, I2, F> core::panic::unwind_safe::UnwindSafe for fuc_engine::CopyOp<'a, 'b, I1, I2, F> where F: core::panic::unwind_safe::UnwindSafe, I1: core::panic::unwind_safe::RefUnwindSafe, I2: core::panic::unwind_safe::RefUnwindSafe impl<T, U> core::convert::Into<U> for fuc_engine::CopyOp<'a, 'b, I1, I2, F> where U: core::convert::From<T> pub fn fuc_engine::CopyOp<'a, 'b, I1, I2, F>::into(self) -> U impl<T, U> core::convert::TryFrom<U> for fuc_engine::CopyOp<'a, 'b, I1, I2, F> where U: core::convert::Into<T> pub type fuc_engine::CopyOp<'a, 'b, I1, I2, F>::Error = core::convert::Infallible pub fn fuc_engine::CopyOp<'a, 'b, I1, I2, F>::try_from(value: U) -> core::result::Result<T, <T as core::convert::TryFrom<U>>::Error> impl<T, U> core::convert::TryInto<U> for fuc_engine::CopyOp<'a, 'b, I1, I2, F> where U: core::convert::TryFrom<T> pub type fuc_engine::CopyOp<'a, 'b, I1, I2, F>::Error = <U as core::convert::TryFrom<T>>::Error pub fn fuc_engine::CopyOp<'a, 'b, I1, I2, F>::try_into(self) -> core::result::Result<U, <U as core::convert::TryFrom<T>>::Error> impl<T> core::any::Any for fuc_engine::CopyOp<'a, 'b, I1, I2, F> where T: 'static + core::marker::Sized pub fn fuc_engine::CopyOp<'a, 'b, I1, I2, F>::type_id(&self) -> core::any::TypeId impl<T> core::borrow::Borrow<T> for fuc_engine::CopyOp<'a, 'b, I1, I2, F> where T: core::marker::Sized pub fn fuc_engine::CopyOp<'a, 'b, I1, I2, F>::borrow(&self) -> &T impl<T> core::borrow::BorrowMut<T> for fuc_engine::CopyOp<'a, 'b, I1, I2, F> where T: core::marker::Sized pub fn fuc_engine::CopyOp<'a, 'b, I1, I2, F>::borrow_mut(&mut self) -> &mut T impl<T> core::convert::From<T> for fuc_engine::CopyOp<'a, 'b, I1, I2, F> pub fn fuc_engine::CopyOp<'a, 'b, I1, I2, F>::from(t: T) -> T impl<T> tracing::instrument::Instrument for fuc_engine::CopyOp<'a, 'b, I1, I2, F> impl<T> tracing::instrument::WithSubscriber for fuc_engine::CopyOp<'a, 'b, I1, I2, F> pub struct fuc_engine::RemoveOp<'a, I: core::convert::Into<alloc::borrow::Cow<'a, std::path::Path>> + 'a, F: core::iter::traits::collect::IntoIterator<Item = I>> impl<'a, I: core::convert::Into<alloc::borrow::Cow<'a, std::path::Path>>, F: core::iter::traits::collect::IntoIterator<Item = I>> fuc_engine::RemoveOp<'a, I, F> pub fn fuc_engine::RemoveOp<'a, I, F>::run(self) -> core::result::Result<(), fuc_engine::Error> impl<'a, I: core::convert::Into<alloc::borrow::Cow<'a, std::path::Path>> + 'a, F: core::iter::traits::collect::IntoIterator<Item = I>> fuc_engine::RemoveOp<'a, I, F> pub fn fuc_engine::RemoveOp<'a, I, F>::builder() -> RemoveOpBuilder<'a, I, F, ((), (), (), ())> impl<'a, I: core::fmt::Debug + core::convert::Into<alloc::borrow::Cow<'a, std::path::Path>> + 'a, F: core::fmt::Debug + core::iter::traits::collect::IntoIterator<Item = I>> core::fmt::Debug for fuc_engine::RemoveOp<'a, I, F> pub fn fuc_engine::RemoveOp<'a, I, F>::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result impl<'a, I, F> core::marker::Freeze for fuc_engine::RemoveOp<'a, I, F> where F: core::marker::Freeze impl<'a, I, F> core::marker::Send for fuc_engine::RemoveOp<'a, I, F> where F: core::marker::Send, I: core::marker::Sync impl<'a, I, F> core::marker::Sync for fuc_engine::RemoveOp<'a, I, F> where F: core::marker::Sync, I: core::marker::Sync impl<'a, I, F> core::marker::Unpin for fuc_engine::RemoveOp<'a, I, F> where F: core::marker::Unpin impl<'a, I, F> core::panic::unwind_safe::RefUnwindSafe for fuc_engine::RemoveOp<'a, I, F> where F: core::panic::unwind_safe::RefUnwindSafe, I: core::panic::unwind_safe::RefUnwindSafe impl<'a, I, F> core::panic::unwind_safe::UnwindSafe for fuc_engine::RemoveOp<'a, I, F> where F: core::panic::unwind_safe::UnwindSafe, I: core::panic::unwind_safe::RefUnwindSafe impl<T, U> core::convert::Into<U> for fuc_engine::RemoveOp<'a, I, F> where U: core::convert::From<T> pub fn fuc_engine::RemoveOp<'a, I, F>::into(self) -> U impl<T, U> core::convert::TryFrom<U> for fuc_engine::RemoveOp<'a, I, F> where U: core::convert::Into<T> pub type fuc_engine::RemoveOp<'a, I, F>::Error = core::convert::Infallible pub fn fuc_engine::RemoveOp<'a, I, F>::try_from(value: U) -> core::result::Result<T, <T as core::convert::TryFrom<U>>::Error> impl<T, U> core::convert::TryInto<U> for fuc_engine::RemoveOp<'a, I, F> where U: core::convert::TryFrom<T> pub type fuc_engine::RemoveOp<'a, I, F>::Error = <U as core::convert::TryFrom<T>>::Error pub fn fuc_engine::RemoveOp<'a, I, F>::try_into(self) -> core::result::Result<U, <U as core::convert::TryFrom<T>>::Error> impl<T> core::any::Any for fuc_engine::RemoveOp<'a, I, F> where T: 'static + core::marker::Sized pub fn fuc_engine::RemoveOp<'a, I, F>::type_id(&self) -> core::any::TypeId impl<T> core::borrow::Borrow<T> for fuc_engine::RemoveOp<'a, I, F> where T: core::marker::Sized pub fn fuc_engine::RemoveOp<'a, I, F>::borrow(&self) -> &T impl<T> core::borrow::BorrowMut<T> for fuc_engine::RemoveOp<'a, I, F> where T: core::marker::Sized pub fn fuc_engine::RemoveOp<'a, I, F>::borrow_mut(&mut self) -> &mut T impl<T> core::convert::From<T> for fuc_engine::RemoveOp<'a, I, F> pub fn fuc_engine::RemoveOp<'a, I, F>::from(t: T) -> T impl<T> tracing::instrument::Instrument for fuc_engine::RemoveOp<'a, I, F> impl<T> tracing::instrument::WithSubscriber for fuc_engine::RemoveOp<'a, I, F> pub fn fuc_engine::copy_file<P: core::convert::AsRef<std::path::Path>, Q: core::convert::AsRef<std::path::Path>>(from: P, to: Q) -> core::result::Result<(), fuc_engine::Error> pub fn fuc_engine::remove_dir_all<P: core::convert::AsRef<std::path::Path>>(path: P) -> core::result::Result<(), fuc_engine::Error> pub fn fuc_engine::remove_file<P: core::convert::AsRef<std::path::Path>>(path: P) -> core::result::Result<(), fuc_engine::Error> 07070100000041000041ED0000000000000000000000026682237800000000000000000000000000000000000000000000001900000000fuc-2.2.0/fuc_engine/src07070100000042000081A40000000000000000000000016682237800000346000000000000000000000000000000000000002000000000fuc-2.2.0/fuc_engine/src/lib.rs#![allow(clippy::used_underscore_binding)] #![allow(clippy::needless_pass_by_value)] use std::{borrow::Cow, io, path::PathBuf}; use thiserror::Error; pub use crate::ops::{copy_file, remove_file, remove_file as remove_dir_all, CopyOp, RemoveOp}; mod ops; #[derive(Error, Debug)] pub enum Error { #[error("An I/O error occurred")] Io { error: io::Error, context: Cow<'static, str>, }, #[error("An attempt was made to delete `/`")] PreserveRoot, #[error("Failed to join thread")] Join, #[error("Invalid file path")] BadPath, #[error("File or directory already exists: {file:?}")] AlreadyExists { file: PathBuf }, #[error("File or directory not found: {file:?}")] NotFound { file: PathBuf }, #[error("An internal bug occurred, please report this")] Internal, } 07070100000043000041ED0000000000000000000000026682237800000000000000000000000000000000000000000000001D00000000fuc-2.2.0/fuc_engine/src/ops07070100000044000081A40000000000000000000000016682237800005784000000000000000000000000000000000000002500000000fuc-2.2.0/fuc_engine/src/ops/copy.rsuse std::{borrow::Cow, fmt::Debug, fs, io, marker::PhantomData, path::Path}; use typed_builder::TypedBuilder; use crate::{ ops::{compat::DirectoryOp, IoErr}, Error, }; /// Copies a file or directory at this path. /// /// # Errors /// /// Returns the underlying I/O errors that occurred. pub fn copy_file<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> Result<(), Error> { CopyOp::builder() .files([(Cow::Borrowed(from.as_ref()), Cow::Borrowed(to.as_ref()))]) .build() .run() } #[derive(TypedBuilder, Debug)] pub struct CopyOp< 'a, 'b, I1: Into<Cow<'a, Path>> + 'a, I2: Into<Cow<'b, Path>> + 'b, F: IntoIterator<Item = (I1, I2)>, > { files: F, #[builder(default = false)] force: bool, #[builder(default = false)] follow_symlinks: bool, #[builder(default)] _marker1: PhantomData<&'a I1>, #[builder(default)] _marker2: PhantomData<&'b I2>, } impl< 'a, 'b, I1: Into<Cow<'a, Path>> + 'a, I2: Into<Cow<'b, Path>> + 'b, F: IntoIterator<Item = (I1, I2)>, > CopyOp<'a, 'b, I1, I2, F> { /// Consume and run this copy operation. /// /// # Errors /// /// Returns the underlying I/O errors that occurred. pub fn run(self) -> Result<(), Error> { let copy = compat::copy_impl(self.follow_symlinks); let result = schedule_copies(self, ©); copy.finish().and(result) } } #[cfg_attr( feature = "tracing", tracing::instrument(level = "trace", skip(files, copy)) )] fn schedule_copies< 'a, 'b, I1: Into<Cow<'a, Path>> + 'a, I2: Into<Cow<'b, Path>> + 'b, F: IntoIterator<Item = (I1, I2)>, >( CopyOp { files, force, follow_symlinks, _marker1: _, _marker2: _, }: CopyOp<'a, 'b, I1, I2, F>, copy: &impl DirectoryOp<(Cow<'a, Path>, Cow<'b, Path>)>, ) -> Result<(), Error> { for (from, to) in files { let from = from.into(); let to = to.into(); if !force { match to.symlink_metadata() { Ok(_) => { return Err(Error::AlreadyExists { file: to.into_owned(), }); } Err(e) if e.kind() == io::ErrorKind::NotFound => { // Do nothing, this is good } r => { r.map_io_err(|| format!("Failed to read metadata for file: {to:?}"))?; } } } let from_metadata = if follow_symlinks { from.metadata() } else { from.symlink_metadata() } .map_io_err(|| format!("Failed to read metadata for file: {from:?}"))?; if let Some(parent) = to.parent() { fs::create_dir_all(parent) .map_io_err(|| format!("Failed to create parent directory: {parent:?}"))?; } #[cfg(unix)] if from_metadata.is_dir() { use std::os::unix::fs::{DirBuilderExt, MetadataExt}; match fs::DirBuilder::new().mode(from_metadata.mode()).create(&to) { Err(e) if force && e.kind() == io::ErrorKind::AlreadyExists => {} r => r.map_io_err(|| format!("Failed to create directory: {to:?}"))?, }; copy.run((from, to))?; } else if from_metadata.is_symlink() { let link = fs::read_link(&from).map_io_err(|| format!("Failed to read symlink: {from:?}"))?; std::os::unix::fs::symlink(link, &to) .map_io_err(|| format!("Failed to create symlink: {to:?}"))?; } else { fs::copy(&from, &to).map_io_err(|| format!("Failed to copy file: {from:?}"))?; } #[cfg(not(unix))] if from_metadata.is_dir() { copy.run((from, to))?; } else { fs::copy(&from, &to).map_io_err(|| format!("Failed to copy file: {from:?}"))?; } } Ok(()) } #[cfg(target_os = "linux")] mod compat { use std::{ borrow::Cow, cell::Cell, env, ffi::{CStr, CString}, fmt::{Debug, Formatter}, fs::File, io, mem::MaybeUninit, num::NonZeroUsize, os::unix::io::{AsFd, OwnedFd}, path::Path, thread, thread::JoinHandle, }; use crossbeam_channel::{Receiver, Sender}; use once_cell::sync::Lazy as LazyCell; use rustix::{ fs::{ copy_file_range, mkdirat, openat, readlinkat, statx, symlinkat, AtFlags, FileType, Mode, OFlags, RawDir, StatxFlags, CWD, }, io::Errno, thread::{unshare, UnshareFlags}, }; use crate::{ ops::{ compat::DirectoryOp, concat_cstrs, get_file_type, join_cstr_paths, path_buf_to_cstring, IoErr, }, Error, }; struct Impl<LF: FnOnce() -> (Sender<TreeNode>, JoinHandle<Result<(), Error>>)> { #[allow(clippy::type_complexity)] scheduling: LazyCell<(Sender<TreeNode>, JoinHandle<Result<(), Error>>), LF>, } pub fn copy_impl<'a, 'b>( follow_symlinks: bool, ) -> impl DirectoryOp<(Cow<'a, Path>, Cow<'b, Path>)> { let scheduling = LazyCell::new(move || { let (tx, rx) = crossbeam_channel::unbounded(); ( tx, thread::spawn(move || root_worker_thread(rx, follow_symlinks)), ) }); Impl { scheduling } } impl<LF: FnOnce() -> (Sender<TreeNode>, JoinHandle<Result<(), Error>>)> DirectoryOp<(Cow<'_, Path>, Cow<'_, Path>)> for Impl<LF> { #[cfg_attr(feature = "tracing", tracing::instrument(level = "trace", skip(self)))] fn run(&self, (from, to): (Cow<Path>, Cow<Path>)) -> Result<(), Error> { let (tasks, _) = &*self.scheduling; tasks .send(TreeNode { from: path_buf_to_cstring(from.into_owned())?, to: path_buf_to_cstring(to.into_owned())?, messages: tasks.clone(), }) .map_err(|_| Error::Internal) } #[cfg_attr(feature = "tracing", tracing::instrument(level = "trace", skip(self)))] fn finish(self) -> Result<(), Error> { let Self { scheduling } = self; if let Ok((tasks, thread)) = LazyCell::into_value(scheduling) { drop(tasks); thread.join().map_err(|_| Error::Join)??; } Ok(()) } } fn unshare_files() -> Result<(), Error> { if env::var_os("NO_UNSHARE").is_none() { unshare(UnshareFlags::FILES).map_io_err(|| "Failed to unshare FD table.")?; } Ok(()) } #[cfg_attr(feature = "tracing", tracing::instrument(level = "trace", skip(tasks)))] fn root_worker_thread(tasks: Receiver<TreeNode>, follow_symlinks: bool) -> Result<(), Error> { unshare_files()?; let mut available_parallelism = thread::available_parallelism() .map(NonZeroUsize::get) .unwrap_or(1) - 1; thread::scope(|scope| { let mut threads = Vec::with_capacity(available_parallelism); { let mut root_to_inode = None; let mut buf = [MaybeUninit::<u8>::uninit(); 8192]; let symlink_buf_cache = Cell::new(Vec::new()); for node in &tasks { let root_to_inode = if let Some(root_to_inode) = root_to_inode { root_to_inode } else { let to_dir = openat( CWD, &node.to, OFlags::RDONLY | OFlags::DIRECTORY | OFlags::PATH, Mode::empty(), ) .map_io_err(|| format!("Failed to open directory: {:?}", node.to))?; let to_metadata = statx(to_dir, c"", AtFlags::EMPTY_PATH, StatxFlags::INO) .map_io_err(|| format!("Failed to stat directory: {:?}", node.to))?; root_to_inode = Some(to_metadata.stx_ino); to_metadata.stx_ino }; let mut maybe_spawn = || { if available_parallelism > 0 && !tasks.is_empty() { #[cfg(feature = "tracing")] tracing::event!( tracing::Level::TRACE, available_parallelism, "Spawning new thread." ); available_parallelism -= 1; threads.push(scope.spawn({ let tasks = tasks.clone(); move || worker_thread(tasks, root_to_inode, follow_symlinks) })); } }; maybe_spawn(); copy_dir( node, root_to_inode, follow_symlinks, &mut buf, &symlink_buf_cache, maybe_spawn, )?; } } for thread in threads { thread.join().map_err(|_| Error::Join)??; } Ok(()) }) } #[cfg_attr(feature = "tracing", tracing::instrument(level = "trace", skip(tasks)))] fn worker_thread( tasks: Receiver<TreeNode>, root_to_inode: u64, follow_symlinks: bool, ) -> Result<(), Error> { unshare_files()?; let mut buf = [MaybeUninit::<u8>::uninit(); 8192]; let symlink_buf_cache = Cell::new(Vec::new()); for node in tasks { copy_dir( node, root_to_inode, follow_symlinks, &mut buf, &symlink_buf_cache, || {}, )?; } Ok(()) } #[cfg_attr( feature = "tracing", tracing::instrument(level = "info", skip(messages, buf, symlink_buf_cache, maybe_spawn)) )] fn copy_dir( TreeNode { from, to, messages }: TreeNode, root_to_inode: u64, follow_symlinks: bool, buf: &mut [MaybeUninit<u8>], symlink_buf_cache: &Cell<Vec<u8>>, mut maybe_spawn: impl FnMut(), ) -> Result<(), Error> { let from_dir = openat( CWD, &from, OFlags::RDONLY | OFlags::DIRECTORY | if follow_symlinks { OFlags::empty() } else { OFlags::NOFOLLOW }, Mode::empty(), ) .map_io_err(|| format!("Failed to open directory: {from:?}"))?; let to_dir = openat( CWD, &to, OFlags::RDONLY | OFlags::DIRECTORY | OFlags::PATH, Mode::empty(), ) .map_io_err(|| format!("Failed to open directory: {to:?}"))?; let mut raw_dir = RawDir::new(&from_dir, buf); while let Some(file) = raw_dir.next() { let file = file.map_io_err(|| format!("Failed to read directory: {from:?}"))?; if file.ino() == root_to_inode { // Block recursive descent from parent into child (e.g. cp parent parent/child). continue; } { let name = file.file_name(); if name == c"." || name == c".." { continue; } } let mut file_type = file.file_type(); if file_type == FileType::Unknown || (follow_symlinks && file_type == FileType::Symlink) { file_type = get_file_type(&from_dir, file.file_name(), &from, follow_symlinks)?; } let file_type = file_type; if file_type == FileType::Directory { let from = concat_cstrs(&from, file.file_name()); let to = concat_cstrs(&to, file.file_name()); copy_one_dir(&from_dir, &from, &to)?; maybe_spawn(); messages .send(TreeNode { from, to, messages: messages.clone(), }) .map_err(|_| Error::Internal)?; } else { copy_one_file( &from_dir, &to_dir, file.file_name(), file_type, &from, &to, symlink_buf_cache, )?; } } Ok(()) } #[cfg_attr( feature = "tracing", tracing::instrument(level = "trace", skip(from_dir)) )] pub fn copy_one_dir( from_dir: impl AsFd, from_path: &CString, to_path: &CString, ) -> Result<(), Error> { let from_mode = { let from_metadata = statx(from_dir, c"", AtFlags::EMPTY_PATH, StatxFlags::MODE) .map_io_err(|| format!("Failed to stat directory: {from_path:?}"))?; Mode::from_raw_mode(from_metadata.stx_mode.into()) }; match mkdirat(CWD, to_path, from_mode) { Err(Errno::EXIST) => {} r => r.map_io_err(|| format!("Failed to create directory: {to_path:?}"))?, }; Ok(()) } #[cfg_attr( feature = "tracing", tracing::instrument(level = "debug", skip(from_dir, to_dir, symlink_buf_cache)) )] fn copy_one_file( from_dir: impl AsFd, to_dir: impl AsFd, file_name: &CStr, file_type: FileType, from_path: &CString, to_path: &CString, symlink_buf_cache: &Cell<Vec<u8>>, ) -> Result<(), Error> { if file_type == FileType::Symlink { copy_symlink( from_dir, to_dir, file_name, from_path, to_path, symlink_buf_cache, ) } else { let (from, to) = prep_regular_file(from_dir, to_dir, file_name, from_path, to_path)?; if file_type == FileType::RegularFile { copy_regular_file(from, to, file_name, from_path) } else { copy_any_file(from, to, file_name, from_path) } } } #[cfg_attr( feature = "tracing", tracing::instrument(level = "trace", skip(from, to)) )] fn copy_regular_file( from: OwnedFd, to: OwnedFd, file_name: &CStr, from_path: &CString, ) -> Result<(), Error> { let mut total_copied = 0; loop { let byte_copied = match copy_file_range(&from, None, &to, None, usize::MAX / 2 - total_copied) { Err(Errno::XDEV) if total_copied == 0 => { return copy_any_file(from, to, file_name, from_path); } r => r.map_io_err(|| { format!( "Failed to copy file: {:?}", join_cstr_paths(from_path, file_name) ) })?, }; if byte_copied == 0 { return Ok(()); } total_copied += byte_copied; } } #[cold] #[cfg_attr( feature = "tracing", tracing::instrument(level = "trace", skip(from, to)) )] fn copy_any_file( from: OwnedFd, to: OwnedFd, file_name: &CStr, from_path: &CString, ) -> Result<(), Error> { io::copy(&mut File::from(from), &mut File::from(to)) .map_io_err(|| { format!( "Failed to copy file: {:?}", join_cstr_paths(from_path, file_name) ) }) .map(|_| ()) } #[cfg_attr( feature = "tracing", tracing::instrument(level = "trace", skip(from_dir, to_dir)) )] fn prep_regular_file( from_dir: impl AsFd, to_dir: impl AsFd, file_name: &CStr, from_path: &CString, to_path: &CString, ) -> Result<(OwnedFd, OwnedFd), Error> { let from = openat(&from_dir, file_name, OFlags::RDONLY, Mode::empty()).map_io_err(|| { format!( "Failed to open file: {:?}", join_cstr_paths(from_path, file_name) ) })?; let to = { let from_mode = { let from_metadata = statx(from_dir, file_name, AtFlags::empty(), StatxFlags::MODE) .map_io_err(|| { format!( "Failed to stat file: {:?}", join_cstr_paths(from_path, file_name) ) })?; Mode::from_raw_mode(from_metadata.stx_mode.into()) }; openat( &to_dir, file_name, OFlags::CREATE | OFlags::TRUNC | OFlags::WRONLY, from_mode, ) .map_io_err(|| { format!( "Failed to open file: {:?}", join_cstr_paths(to_path, file_name) ) })? }; Ok((from, to)) } #[cold] #[cfg_attr( feature = "tracing", tracing::instrument(level = "trace", skip(from_dir, to_dir, symlink_buf_cache)) )] fn copy_symlink( from_dir: impl AsFd, to_dir: impl AsFd, file_name: &CStr, from_path: &CString, to_path: &CString, symlink_buf_cache: &Cell<Vec<u8>>, ) -> Result<(), Error> { let from_symlink = readlinkat(from_dir, file_name, symlink_buf_cache.take()).map_io_err(|| { format!( "Failed to read symlink: {:?}", join_cstr_paths(from_path, file_name) ) })?; symlinkat(&from_symlink, &to_dir, file_name).map_io_err(|| { format!( "Failed to create symlink: {:?}", join_cstr_paths(to_path, file_name) ) })?; symlink_buf_cache.set(from_symlink.into_bytes_with_nul()); Ok(()) } struct TreeNode { from: CString, to: CString, messages: Sender<TreeNode>, } impl Debug for TreeNode { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { f.debug_struct("TreeNode") .field("from", &self.from) .field("to", &self.to) .finish_non_exhaustive() } } } #[cfg(not(target_os = "linux"))] mod compat { use std::{borrow::Cow, fs, io, path::Path}; use rayon::prelude::*; use crate::{ ops::{compat::DirectoryOp, IoErr}, Error, }; struct Impl { #[allow(dead_code)] follow_symlinks: bool, } pub fn copy_impl<'a, 'b>( follow_symlinks: bool, ) -> impl DirectoryOp<(Cow<'a, Path>, Cow<'b, Path>)> { Impl { follow_symlinks } } impl DirectoryOp<(Cow<'_, Path>, Cow<'_, Path>)> for Impl { fn run(&self, (from, to): (Cow<Path>, Cow<Path>)) -> Result<(), Error> { copy_dir( &from, to, #[cfg(unix)] self.follow_symlinks, #[cfg(unix)] None, ) .map_io_err(|| format!("Failed to copy directory: {from:?}")) } fn finish(self) -> Result<(), Error> { Ok(()) } } fn copy_dir<P: AsRef<Path>, Q: AsRef<Path>>( from: P, to: Q, #[cfg(unix)] follow_symlinks: bool, #[cfg(unix)] root_to_inode: Option<u64>, ) -> Result<(), io::Error> { let to = to.as_ref(); match fs::create_dir(to) { Err(e) if e.kind() == io::ErrorKind::AlreadyExists => {} r => r?, }; #[cfg(unix)] let root_to_inode = Some(maybe_compute_root_to_inode(to, root_to_inode)?); from.as_ref() .read_dir()? .par_bridge() .try_for_each(|dir_entry| -> io::Result<()> { let dir_entry = dir_entry?; #[cfg(unix)] { use std::os::unix::fs::DirEntryExt; if Some(dir_entry.ino()) == root_to_inode { return Ok(()); } } let to = to.join(dir_entry.file_name()); #[allow(unused_mut)] let mut file_type = dir_entry.file_type()?; #[cfg(unix)] if follow_symlinks && file_type.is_symlink() { file_type = fs::metadata(dir_entry.path())?.file_type(); } let file_type = file_type; #[cfg(unix)] if file_type.is_dir() { copy_dir(dir_entry.path(), to, follow_symlinks, root_to_inode)?; } else if file_type.is_symlink() { std::os::unix::fs::symlink(fs::read_link(dir_entry.path())?, to)?; } else { fs::copy(dir_entry.path(), to)?; } #[cfg(not(unix))] if file_type.is_dir() { copy_dir(dir_entry.path(), to)?; } else { fs::copy(dir_entry.path(), to)?; } Ok(()) }) } #[cfg(unix)] fn maybe_compute_root_to_inode<P: AsRef<Path>>( to: P, root_to_inode: Option<u64>, ) -> Result<u64, io::Error> { Ok(if let Some(ino) = root_to_inode { ino } else { use std::os::unix::fs::MetadataExt; fs::metadata(to)?.ino() }) } } 07070100000045000081A40000000000000000000000016682237800000CBC000000000000000000000000000000000000002400000000fuc-2.2.0/fuc_engine/src/ops/mod.rsuse std::{borrow::Cow, io}; pub use copy::{copy_file, CopyOp}; #[cfg(target_os = "linux")] use linux::{concat_cstrs, get_file_type, join_cstr_paths, path_buf_to_cstring}; pub use remove::{remove_file, RemoveOp}; use crate::Error; mod copy; mod remove; trait IoErr<Out> { fn map_io_err<I: Into<Cow<'static, str>>>(self, f: impl FnOnce() -> I) -> Out; } impl<T> IoErr<Result<T, Error>> for Result<T, io::Error> { fn map_io_err<I: Into<Cow<'static, str>>>( self, context: impl FnOnce() -> I, ) -> Result<T, Error> { self.map_err(|error| Error::Io { error, context: context().into(), }) } } #[cfg(target_os = "linux")] mod linux { use std::{ borrow::Cow, ffi::{CStr, CString, OsStr, OsString}, io, os::unix::{ ffi::{OsStrExt, OsStringExt}, io::AsFd, }, path::{Path, PathBuf, MAIN_SEPARATOR}, }; use rustix::fs::{statx, AtFlags, FileType, StatxFlags}; use crate::{ops::IoErr, Error}; impl<T> IoErr<Result<T, Error>> for Result<T, rustix::io::Errno> { fn map_io_err<I: Into<Cow<'static, str>>>( self, context: impl FnOnce() -> I, ) -> Result<T, Error> { self.map_err(io::Error::from).map_io_err(context) } } #[cfg_attr(feature = "tracing", tracing::instrument(level = "trace"))] pub fn path_buf_to_cstring(buf: PathBuf) -> Result<CString, Error> { CString::new(OsString::from(buf).into_vec()).map_err(|_| Error::BadPath) } #[cfg_attr(feature = "tracing", tracing::instrument(level = "trace"))] pub fn concat_cstrs(prefix: &CString, name: &CStr) -> CString { let prefix = prefix.as_bytes(); let name = name.to_bytes_with_nul(); let mut path = Vec::with_capacity(prefix.len() + 1 + name.len()); path.extend_from_slice(prefix); path.push(u8::try_from(MAIN_SEPARATOR).unwrap()); path.extend_from_slice(name); unsafe { CString::from_vec_with_nul_unchecked(path) } } #[cfg_attr(feature = "tracing", tracing::instrument(level = "trace"))] pub fn join_cstr_paths(path: &CString, name: &CStr) -> PathBuf { Path::new(OsStr::from_bytes(path.as_bytes())) .join(Path::new(OsStr::from_bytes(name.to_bytes()))) } #[cold] #[cfg_attr(feature = "tracing", tracing::instrument(level = "trace", skip(dir)))] pub fn get_file_type( dir: impl AsFd, file_name: &CStr, path: &CString, follow_symlinks: bool, ) -> Result<FileType, Error> { let flags = if follow_symlinks { AtFlags::empty() } else { AtFlags::SYMLINK_NOFOLLOW }; statx(dir, file_name, flags, StatxFlags::TYPE) .map_io_err(|| { format!( "Failed to stat file: {:?}", join_cstr_paths(path, file_name) ) }) .map(|metadata| FileType::from_raw_mode(metadata.stx_mode.into())) } } mod compat { use crate::Error; pub trait DirectoryOp<T> { fn run(&self, dir: T) -> Result<(), Error>; fn finish(self) -> Result<(), Error>; } } 07070100000046000081A40000000000000000000000016682237800003C13000000000000000000000000000000000000002700000000fuc-2.2.0/fuc_engine/src/ops/remove.rsuse std::{ borrow::Cow, ffi::OsStr, fmt::Debug, fs, io, marker::PhantomData, path::{Path, MAIN_SEPARATOR_STR}, }; use typed_builder::TypedBuilder; use crate::{ ops::{compat::DirectoryOp, IoErr}, Error, }; /// Removes a file or directory at this path, after removing all its contents. /// /// This function does **not** follow symbolic links: it will simply remove /// the symbolic link itself. /// /// # Errors /// /// Returns the underlying I/O errors that occurred. pub fn remove_file<P: AsRef<Path>>(path: P) -> Result<(), Error> { RemoveOp::builder() .files([Cow::Borrowed(path.as_ref())]) .build() .run() } #[derive(TypedBuilder, Debug)] pub struct RemoveOp<'a, I: Into<Cow<'a, Path>> + 'a, F: IntoIterator<Item = I>> { files: F, #[builder(default = false)] force: bool, #[builder(default = true)] preserve_root: bool, #[builder(default)] _marker: PhantomData<&'a I>, } impl<'a, I: Into<Cow<'a, Path>>, F: IntoIterator<Item = I>> RemoveOp<'a, I, F> { /// Consume and run this remove operation. /// /// # Errors /// /// Returns the underlying I/O errors that occurred. pub fn run(self) -> Result<(), Error> { let remove = compat::remove_impl(); let result = schedule_deletions(self, &remove); remove.finish().and(result) } } #[cfg_attr( feature = "tracing", tracing::instrument(level = "trace", skip(files, remove)) )] fn schedule_deletions<'a, I: Into<Cow<'a, Path>>, F: IntoIterator<Item = I>>( RemoveOp { files, force, preserve_root, _marker: _, }: RemoveOp<'a, I, F>, remove: &impl DirectoryOp<Cow<'a, Path>>, ) -> Result<(), Error> { for file in files { let file = file.into(); if preserve_root && file == Path::new("/") { return Err(Error::PreserveRoot); } let stripped_path = { let trailing_slash_stripped = file .as_os_str() .as_encoded_bytes() .strip_suffix(MAIN_SEPARATOR_STR.as_bytes()) .unwrap_or(file.as_os_str().as_encoded_bytes()); let path = unsafe { OsStr::from_encoded_bytes_unchecked(trailing_slash_stripped) }; Path::new(path) }; let is_dir = match stripped_path.symlink_metadata() { Err(e) if e.kind() == io::ErrorKind::NotFound => { if force { continue; } return Err(Error::NotFound { file: stripped_path.to_path_buf(), }); } r => r, } .map_io_err(|| format!("Failed to read metadata for file: {stripped_path:?}"))? .is_dir(); if is_dir { remove.run( if file.as_os_str().len() == stripped_path.as_os_str().len() { file } else { Cow::Owned(stripped_path.to_path_buf()) }, )?; } else { fs::remove_file(stripped_path) .map_io_err(|| format!("Failed to delete file: {stripped_path:?}"))?; } } Ok(()) } #[cfg(target_os = "linux")] mod compat { use std::{ borrow::Cow, env, env::{current_dir, set_current_dir}, ffi::{CStr, CString, OsStr}, fmt::{Debug, Formatter}, fs, mem::MaybeUninit, num::NonZeroUsize, os::{ fd::{AsFd, OwnedFd}, unix::ffi::OsStrExt, }, path::{Path, PathBuf}, sync::Arc, thread, thread::JoinHandle, }; use crossbeam_channel::{Receiver, Sender}; use once_cell::sync::Lazy as LazyCell; use rustix::{ fs::{openat, unlinkat, AtFlags, FileType, Mode, OFlags, RawDir, CWD}, thread::{unshare, UnshareFlags}, }; use crate::{ ops::{ compat::DirectoryOp, concat_cstrs, get_file_type, join_cstr_paths, path_buf_to_cstring, IoErr, }, Error, }; struct Impl<LF: FnOnce() -> (Sender<TreeNode>, JoinHandle<Result<(), Error>>)> { #[allow(clippy::type_complexity)] scheduling: LazyCell<(Sender<TreeNode>, JoinHandle<Result<(), Error>>), LF>, } pub fn remove_impl<'a>() -> impl DirectoryOp<Cow<'a, Path>> { let scheduling = LazyCell::new(|| { let (tx, rx) = crossbeam_channel::unbounded(); (tx, thread::spawn(|| root_worker_thread(rx))) }); Impl { scheduling } } impl<LF: FnOnce() -> (Sender<TreeNode>, JoinHandle<Result<(), Error>>)> DirectoryOp<Cow<'_, Path>> for Impl<LF> { #[cfg_attr(feature = "tracing", tracing::instrument(level = "trace", skip(self)))] fn run(&self, dir: Cow<Path>) -> Result<(), Error> { let Self { ref scheduling } = *self; let (tasks, _) = &**scheduling; tasks .send(TreeNode { path: path_buf_to_cstring(dir.into_owned())?, parent: None, messages: tasks.clone(), }) .map_err(|_| Error::Internal) } #[cfg_attr(feature = "tracing", tracing::instrument(level = "trace", skip(self)))] fn finish(self) -> Result<(), Error> { let Self { scheduling } = self; if let Ok((tasks, thread)) = LazyCell::into_value(scheduling) { drop(tasks); thread.join().map_err(|_| Error::Join)??; } Ok(()) } } fn unshare_io() -> Result<(), Error> { if env::var_os("NO_UNSHARE").is_none() { unshare(UnshareFlags::FILES | UnshareFlags::FS) .map_io_err(|| "Failed to unshare I/O.")?; } Ok(()) } #[cfg_attr(feature = "tracing", tracing::instrument(level = "trace", skip(tasks)))] fn root_worker_thread(tasks: Receiver<TreeNode>) -> Result<(), Error> { unshare_io()?; let mut available_parallelism = thread::available_parallelism() .map(NonZeroUsize::get) .unwrap_or(1) - 1; thread::scope(|scope| { let mut threads = Vec::with_capacity(available_parallelism); { let mut buf = [MaybeUninit::<u8>::uninit(); 8192]; for message in &tasks { let mut maybe_spawn = || { if available_parallelism > 0 && !tasks.is_empty() { #[cfg(feature = "tracing")] tracing::event!( tracing::Level::TRACE, available_parallelism, "Spawning new thread." ); available_parallelism -= 1; threads.push(scope.spawn({ let tasks = tasks.clone(); || worker_thread(tasks) })); } }; maybe_spawn(); delete_dir(message, &mut buf, maybe_spawn)?; } } for thread in threads { thread.join().map_err(|_| Error::Join)??; } Ok(()) }) } #[cfg_attr(feature = "tracing", tracing::instrument(level = "trace", skip(tasks)))] fn worker_thread(tasks: Receiver<TreeNode>) -> Result<(), Error> { unshare_io()?; let mut buf = [MaybeUninit::<u8>::uninit(); 8192]; for message in tasks { delete_dir(message, &mut buf, || {})?; } Ok(()) } #[cfg_attr( feature = "tracing", tracing::instrument(level = "info", skip(buf, maybe_spawn)) )] fn delete_dir( node: TreeNode, buf: &mut [MaybeUninit<u8>], maybe_spawn: impl FnMut(), ) -> Result<(), Error> { let dir = openat( CWD, &node.path, OFlags::RDONLY | OFlags::DIRECTORY | OFlags::NOFOLLOW, Mode::empty(), ) .map_io_err(|| format!("Failed to open directory: {:?}", node.path))?; let node = delete_dir_contents(node, dir, buf, maybe_spawn)?; delete_empty_dir_chain(node) } #[cfg_attr( feature = "tracing", tracing::instrument(level = "trace", skip(dir, buf, maybe_spawn)) )] fn delete_dir_contents( node: TreeNode, dir: OwnedFd, buf: &mut [MaybeUninit<u8>], mut maybe_spawn: impl FnMut(), ) -> Result<Option<TreeNode>, Error> { enum Arcable<T> { Raw(T), Arced(Arc<T>), } impl<T> Arcable<T> { fn into_inner(this: Self) -> Option<T> { match this { Self::Raw(t) => Some(t), Self::Arced(arc) => Arc::into_inner(arc), } } } impl<T> AsRef<T> for Arcable<T> { fn as_ref(&self) -> &T { match self { Self::Raw(node) => node, Self::Arced(arc) => arc, } } } let mut node = Arcable::Raw(node); let mut raw_dir = RawDir::new(&dir, buf); while let Some(file) = raw_dir.next() { let file = file.map_io_err(|| format!("Failed to read directory: {:?}", node.as_ref().path))?; { let name = file.file_name(); if name == c"." || name == c".." { continue; } } let file_type = match file.file_type() { FileType::Unknown => { get_file_type(&dir, file.file_name(), &node.as_ref().path, false)? } t => t, }; if file_type == FileType::Directory { if node.as_ref().path.as_bytes_with_nul().len() + file.file_name().count_bytes() > 4096 { long_path_fallback_deletion(&node.as_ref().path, file.file_name())?; continue; } maybe_spawn(); let node = match node { Arcable::Raw(raw) => { let arc = Arc::new(raw); node = Arcable::Arced(arc.clone()); arc } Arcable::Arced(ref node) => node.clone(), }; node.messages .send(TreeNode { path: concat_cstrs(&node.path, file.file_name()), parent: Some(node.clone()), messages: node.messages.clone(), }) .map_err(|_| Error::Internal)?; } else { delete_file(node.as_ref(), &dir, file.file_name())?; } } Ok(Arcable::into_inner(node)) } #[cfg_attr(feature = "tracing", tracing::instrument(level = "trace"))] fn delete_empty_dir_chain(mut node: Option<TreeNode>) -> Result<(), Error> { let mut result = Ok(()); while let Some(TreeNode { ref path, parent, messages: _, }) = node { if result.is_ok() { result = unlinkat(CWD, path, AtFlags::REMOVEDIR) .map_io_err(|| format!("Failed to delete directory: {path:?}")); } node = parent.and_then(Arc::into_inner); } result } #[cfg_attr(feature = "tracing", tracing::instrument(level = "debug", skip(dir)))] fn delete_file(node: &TreeNode, dir: impl AsFd, file: &CStr) -> Result<(), Error> { unlinkat(&dir, file, AtFlags::empty()).map_io_err(|| { format!( "Failed to delete file: {:?}", join_cstr_paths(&node.path, file) ) }) } #[cold] #[cfg_attr(feature = "tracing", tracing::instrument(level = "trace"))] fn long_path_fallback_deletion(parent: &CString, child: &CStr) -> Result<(), Error> { struct CurrentDir(PathBuf); impl CurrentDir { fn new() -> Result<Self, Error> { Ok(Self( current_dir().map_io_err(|| "Failed to get current directory")?, )) } } impl Drop for CurrentDir { fn drop(&mut self) { set_current_dir(&self.0).expect("Failed to restore current dir"); } } let _guard = CurrentDir::new()?; { let parent = Path::new(OsStr::from_bytes(parent.as_bytes())); set_current_dir(parent) .map_io_err(|| format!("Failed to set current directory: {parent:?}"))?; } { let child = Path::new(OsStr::from_bytes(child.to_bytes())); fs::remove_dir_all(child) .map_io_err(|| format!("Failed to delete directory and its contents: {child:?}"))?; } Ok(()) } struct TreeNode { path: CString, parent: Option<Arc<TreeNode>>, messages: Sender<TreeNode>, } impl Debug for TreeNode { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { self.path.fmt(f) } } } #[cfg(all(not(target_os = "linux"), not(target_os = "windows")))] mod compat { use std::{borrow::Cow, fs, io, path::Path}; use rayon::prelude::*; use crate::{ ops::{compat::DirectoryOp, IoErr}, Error, }; struct Impl; pub fn remove_impl<'a>() -> impl DirectoryOp<Cow<'a, Path>> { Impl } impl DirectoryOp<Cow<'_, Path>> for Impl { fn run(&self, dir: Cow<Path>) -> Result<(), Error> { remove_dir_all(&dir).map_io_err(|| format!("Failed to delete directory: {dir:?}")) } fn finish(self) -> Result<(), Error> { Ok(()) } } fn remove_dir_all<P: AsRef<Path>>(path: P) -> Result<(), io::Error> { let path = path.as_ref(); path.read_dir()? .par_bridge() .try_for_each(|dir_entry| -> io::Result<()> { let dir_entry = dir_entry?; if dir_entry.file_type()?.is_dir() { remove_dir_all(dir_entry.path())?; } else { fs::remove_file(dir_entry.path())?; } Ok(()) })?; fs::remove_dir(path) } } #[cfg(target_os = "windows")] mod compat { use std::{borrow::Cow, path::Path}; use remove_dir_all::remove_dir_all; use crate::{ ops::{compat::DirectoryOp, IoErr}, Error, }; struct Impl; pub fn remove_impl<'a>() -> impl DirectoryOp<Cow<'a, Path>> { Impl } impl DirectoryOp<Cow<'_, Path>> for Impl { fn run(&self, dir: Cow<Path>) -> Result<(), Error> { remove_dir_all(&dir).map_io_err(|| format!("Failed to delete directory: {dir:?}")) } fn finish(self) -> Result<(), Error> { Ok(()) } } } 07070100000047000041ED0000000000000000000000026682237800000000000000000000000000000000000000000000001B00000000fuc-2.2.0/fuc_engine/tests07070100000048000081A4000000000000000000000001668223780000004E000000000000000000000000000000000000002200000000fuc-2.2.0/fuc_engine/tests/api.rs#[test] #[cfg(target_os = "linux")] fn api() { supercilex_tests::api(); } 07070100000049000081A4000000000000000000000001668223780000188F000000000000000000000000000000000000002300000000fuc-2.2.0/fuc_engine/tests/copy.rsuse std::{borrow::Cow, fs, fs::File}; use rstest::rstest; use tempfile::tempdir; #[test] fn pre_existing_file_no_force() { let root = tempdir().unwrap(); let from = root.path().join("from"); File::create(&from).unwrap(); let to = root.path().join("to"); File::create(&to).unwrap(); fuc_engine::CopyOp::builder() .files([(Cow::Owned(from), Cow::Owned(to))]) .force(false) .build() .run() .unwrap_err(); } #[test] fn pre_existing_file_force() { let root = tempdir().unwrap(); let from = root.path().join("from"); File::create(&from).unwrap(); let to = root.path().join("to"); File::create(&to).unwrap(); fuc_engine::CopyOp::builder() .files([(Cow::Owned(from), Cow::Owned(to))]) .force(true) .build() .run() .unwrap(); } #[test] fn pre_existing_dir_force() { let root = tempdir().unwrap(); let from = root.path().join("from"); fs::create_dir(&from).unwrap(); File::create(from.join("a")).unwrap(); File::create(from.join("b")).unwrap(); let to = root.path().join("to"); fs::create_dir(&to).unwrap(); File::create(to.join("b")).unwrap(); File::create(to.join("c")).unwrap(); fuc_engine::CopyOp::builder() .files([(Cow::Owned(from), Cow::Borrowed(to.as_path()))]) .force(true) .build() .run() .unwrap(); assert!(to.join("a").exists()); assert!(to.join("b").exists()); assert!(to.join("c").exists()); } #[test] #[cfg(unix)] fn self_nested() { let root = tempdir().unwrap(); let from = root.path().join("from"); fs::create_dir(&from).unwrap(); let to = root.path().join("from/to"); fuc_engine::CopyOp::builder() .files([(Cow::Owned(from), Cow::Borrowed(to.as_path()))]) .force(true) .build() .run() .unwrap(); assert!(to.exists()); } #[test] fn non_existent_parent_dir() { let root = tempdir().unwrap(); let from = root.path().join("from"); fs::create_dir(&from).unwrap(); let to = root.path().join("a/b/c/to"); fuc_engine::CopyOp::builder() .files([(Cow::Owned(from), Cow::Borrowed(to.as_path()))]) .force(true) .build() .run() .unwrap(); assert!(to.exists()); } #[test] fn one_file() { let root = tempdir().unwrap(); let from = root.path().join("from"); File::create(&from).unwrap(); let to = root.path().join("to"); fuc_engine::copy_file(&from, &to).unwrap(); assert!(to.exists()); } #[test] fn one_dir() { let root = tempdir().unwrap(); let from = root.path().join("from"); fs::create_dir(&from).unwrap(); File::create(from.join("file")).unwrap(); let to = root.path().join("to"); fuc_engine::copy_file(&from, &to).unwrap(); assert!(to.exists()); } #[test] #[cfg(unix)] fn symbolic_link_copy_dir() { let root = tempdir().unwrap(); let from = root.path().join("dir"); fs::create_dir(&from).unwrap(); std::os::unix::fs::symlink(".", from.join("file")).unwrap(); let to = root.path().join("to"); fuc_engine::copy_file(&from, &to).unwrap(); assert!(to.exists()); } #[test] #[cfg(unix)] fn symbolic_link_copy_link() { let root = tempdir().unwrap(); let from = root.path().join("from"); std::os::unix::fs::symlink(".", &from).unwrap(); let to = root.path().join("to"); fuc_engine::copy_file(&from, &to).unwrap(); assert!(to.exists()); } #[rstest] #[cfg(unix)] fn dereference_symbolic_link_to_regular_file(#[values(false, true)] follow_symlinks: bool) { let root = tempdir().unwrap(); let from = root.path().join("from"); File::create(from).unwrap(); let link = root.path().join("link"); std::os::unix::fs::symlink("from", &link).unwrap(); let to = root.path().join("to"); fuc_engine::CopyOp::builder() .files([(Cow::Owned(link), Cow::Borrowed(to.as_path()))]) .follow_symlinks(follow_symlinks) .build() .run() .unwrap(); if follow_symlinks { assert!(to.symlink_metadata().unwrap().is_file()); } else { assert!(to.symlink_metadata().unwrap().is_symlink()); } } #[rstest] #[cfg(unix)] fn dereference_symbolic_link_to_regular_file_in_dir(#[values(false, true)] follow_symlinks: bool) { let root = tempdir().unwrap(); let from = root.path().join("from"); fs::create_dir(&from).unwrap(); File::create(from.join("file")).unwrap(); std::os::unix::fs::symlink("file", from.join("link")).unwrap(); let to = root.path().join("to"); fuc_engine::CopyOp::builder() .files([(Cow::Owned(from), Cow::Borrowed(to.as_path()))]) .follow_symlinks(follow_symlinks) .build() .run() .unwrap(); assert!(to.join("file").symlink_metadata().unwrap().is_file()); if follow_symlinks { assert!(to.join("link").symlink_metadata().unwrap().is_file()); } else { assert!(to.join("link").symlink_metadata().unwrap().is_symlink()); } } #[rstest] #[cfg(unix)] fn dereference_symbolic_link_to_dir_in_dir(#[values(false, true)] follow_symlinks: bool) { let root = tempdir().unwrap(); let from = root.path().join("from"); fs::create_dir(&from).unwrap(); fs::create_dir(from.join("subdir")).unwrap(); File::create(from.join("subdir/file")).unwrap(); std::os::unix::fs::symlink("subdir", from.join("subdirlink")).unwrap(); let to = root.path().join("to"); fuc_engine::CopyOp::builder() .files([(Cow::Owned(from), Cow::Borrowed(to.as_path()))]) .follow_symlinks(follow_symlinks) .build() .run() .unwrap(); assert!(to.join("subdir").symlink_metadata().unwrap().is_dir()); assert!(to.join("subdir/file").symlink_metadata().unwrap().is_file()); if follow_symlinks { assert!(to.join("subdirlink").symlink_metadata().unwrap().is_dir()); assert!( to.join("subdirlink/file") .symlink_metadata() .unwrap() .is_file() ); } else { assert!( to.join("subdirlink") .symlink_metadata() .unwrap() .is_symlink() ); } } 0707010000004A000081A40000000000000000000000016682237800000CD6000000000000000000000000000000000000002500000000fuc-2.2.0/fuc_engine/tests/remove.rsuse std::{borrow::Cow, fs, fs::File, io, num::NonZeroU64}; use ftzz::{Generator, NumFilesWithRatio}; use io_adapters::WriteExtension; use rstest::rstest; use tempfile::tempdir; #[test] fn non_existent_file_no_force() { let root = tempdir().unwrap(); let file = root.path().join("file"); fuc_engine::RemoveOp::builder() .files([Cow::Borrowed(file.as_path())]) .force(false) .build() .run() .unwrap_err(); assert!(!file.exists()); assert!(root.path().exists()); } #[test] fn non_existent_file_force() { let root = tempdir().unwrap(); let file = root.path().join("file"); fuc_engine::RemoveOp::builder() .files([Cow::Borrowed(file.as_path())]) .force(true) .build() .run() .unwrap(); assert!(!file.exists()); assert!(root.path().exists()); } #[test] fn one_file() { let root = tempdir().unwrap(); let file = root.path().join("file"); File::create(&file).unwrap(); assert!(file.exists()); fuc_engine::remove_file(&file).unwrap(); assert!(!file.exists()); assert!(root.path().exists()); } #[test] fn one_dir() { let root = tempdir().unwrap(); let dir = root.path().join("dir"); fs::create_dir(&dir).unwrap(); assert!(dir.exists()); fuc_engine::remove_file(&dir).unwrap(); assert!(!dir.exists()); assert!(root.path().exists()); } #[test] #[cfg(unix)] fn symbolic_link_delete_dir() { let root = tempdir().unwrap(); let dir = root.path().join("dir"); fs::create_dir(&dir).unwrap(); let file = dir.join("file"); std::os::unix::fs::symlink(".", &file).unwrap(); assert!(file.exists()); fuc_engine::remove_file(&dir).unwrap(); assert!(!file.exists()); assert!(root.path().exists()); } #[test] #[cfg(unix)] fn symbolic_link_delete_link() { let root = tempdir().unwrap(); let file = root.path().join("file"); std::os::unix::fs::symlink(".", &file).unwrap(); assert!(file.exists()); fuc_engine::remove_file(&file).unwrap(); assert!(!file.exists()); assert!(root.path().exists()); } #[test] #[cfg(target_os = "linux")] fn extremely_long_file_name() { use std::{ env::{current_dir, set_current_dir}, fs::create_dir, }; let cwd = current_dir().unwrap(); let root = tempdir().unwrap(); set_current_dir(&root).unwrap(); for _ in 0..10_000 { create_dir("dir").unwrap(); set_current_dir("dir").unwrap(); } File::create("file").unwrap(); set_current_dir(cwd).unwrap(); let target = root.path().join("dir"); assert!(target.exists()); fuc_engine::remove_file(&target).unwrap(); assert!(!target.exists()); assert!(root.path().exists()); } #[rstest] fn uniform(#[values(1_000, 100_000)] num_files: u64) { let root = tempdir().unwrap(); let dir = root.path().join("dir"); Generator::builder() .root_dir(dir.clone()) .num_files_with_ratio(NumFilesWithRatio::from_num_files( NonZeroU64::new(num_files).unwrap(), )) .build() .generate(&mut io::sink().write_adapter()) .unwrap(); fuc_engine::remove_file(&dir).unwrap(); assert!(!dir.exists()); assert!(root.path().exists()); } 0707010000004B000041ED0000000000000000000000026682237800000000000000000000000000000000000000000000000E00000000fuc-2.2.0/rmz0707010000004C000081A40000000000000000000000016682237800000648000000000000000000000000000000000000001900000000fuc-2.2.0/rmz/Cargo.toml[package] name = "rmz" version.workspace = true authors.workspace = true edition.workspace = true description = "Fast rm provides an alternative to rm that focuses on maximizing performance." repository.workspace = true keywords = ["tools", "files", "rm"] categories = ["command-line-utilities", "development-tools", "filesystem"] license.workspace = true rust-version.workspace = true [dependencies] clap = { version = "4.5.8", features = ["derive", "wrap_help"] } error-stack = "0.4.1" fuc_engine = { version = "2", path = "../fuc_engine" } indicatif = { version = "0.17.8", optional = true } thiserror = "1.0.61" tracing = { version = "0.1.40", optional = true } tracing-indicatif = { version = "0.3.6", optional = true } tracing-subscriber = { version = "0.3.18", features = ["env-filter"], optional = true } tracing-tracy = { version = "0.11.0", features = ["flush-on-exit"], optional = true } tracy-client = { version = "0.17.0", optional = true } [dev-dependencies] criterion = "0.5.1" ftzz = "3.0.0" io-adapters = "0.3.0" remove_dir_all = { version = "0.8.2", features = ["parallel"] } rm_og_crappy = { path = "../comparisons/rm_og_crappy" } rm_rayon = { path = "../comparisons/rm_rayon" } supercilex-tests = { version = "0.4.6", default-features = false, features = ["clap"] } tempfile = "3.10.1" trycmd = "0.15.4" [features] trace = ["fuc_engine/tracing", "dep:tracing", "dep:tracing-subscriber", "dep:tracing-tracy", "dep:tracy-client"] progress = ["fuc_engine/tracing", "dep:tracing", "dep:tracing-subscriber", "dep:tracing-indicatif", "dep:indicatif"] [[bench]] name = "rmz" harness = false 0707010000004D000041ED0000000000000000000000026682237800000000000000000000000000000000000000000000001800000000fuc-2.2.0/rmz/README.in0707010000004E000041ED0000000000000000000000026682237800000000000000000000000000000000000000000000001C00000000fuc-2.2.0/rmz/README.in/dir0707010000004F000081A40000000000000000000000016682237800000000000000000000000000000000000000000000002000000000fuc-2.2.0/rmz/README.in/dir/bar07070100000050000081A40000000000000000000000016682237800000000000000000000000000000000000000000000001C00000000fuc-2.2.0/rmz/README.in/foo07070100000051000081A400000000000000000000000166822378000004B3000000000000000000000000000000000000001800000000fuc-2.2.0/rmz/README.md# rm zippy [![Crates.io](https://img.shields.io/crates/v/rmz)](https://crates.io/crates/rmz) A zippy alternative to `rm`, a tool to remove files and directories. ## Installation ### Use prebuilt binaries Binaries for a number of platforms are available on the [release page](https://github.com/SUPERCILEX/fuc/releases/latest). ### Build from source ```console,ignore $ cargo install rmz ``` > To install cargo, follow > [these instructions](https://doc.rust-lang.org/cargo/getting-started/installation.html). ## Usage Background: https://github.com/SUPERCILEX/fuc/blob/master/README.md Delete a file: ```console $ rmz foo ``` Delete a directory: ```console $ rmz dir ``` Ignore non-existent files: ```console $ rmz -f non-existent ``` More details: ```console $ rmz --help A zippy alternative to `rm`, a tool to remove files and directories Usage: rmz[EXE] [OPTIONS] <FILES>... Arguments: <FILES>... The files and/or directories to be removed Options: -f, --force Ignore non-existent arguments --no-preserve-root Allow deletion of `/` -h, --help Print help (use `-h` for a summary) -V, --version Print version ``` 07070100000052000041ED0000000000000000000000026682237800000000000000000000000000000000000000000000001900000000fuc-2.2.0/rmz/README.out07070100000053000081A40000000000000000000000016682237800000000000000000000000000000000000000000000001F00000000fuc-2.2.0/rmz/README.out/.keep07070100000054000041ED0000000000000000000000026682237800000000000000000000000000000000000000000000001600000000fuc-2.2.0/rmz/benches07070100000055000081A40000000000000000000000016682237800001CE5000000000000000000000000000000000000001D00000000fuc-2.2.0/rmz/benches/rmz.rsuse std::{fs, fs::File, io, num::NonZeroU64, path::Path, time::Duration}; use criterion::{ criterion_group, criterion_main, measurement::WallTime, AxisScale, BenchmarkGroup, BenchmarkId, Criterion, PlotConfiguration, Throughput, }; use ftzz::{Generator, NumFilesWithRatio}; use io_adapters::WriteExtension; use tempfile::tempdir; fn uniform(c: &mut Criterion) { fn gen_files(dir: &Path, num_files: u64) { Generator::builder() .root_dir(dir.to_path_buf()) .num_files_with_ratio(NumFilesWithRatio::from_num_files( NonZeroU64::new(num_files).unwrap(), )) .build() .generate(&mut io::sink().write_adapter()) .unwrap(); } let mut group = c.benchmark_group("uniform"); group.plot_config(PlotConfiguration::default().summary_scale(AxisScale::Logarithmic)); for num_files in [10, 1_000, 100_000, 1_000_000] { group.sample_size(10); group.throughput(Throughput::Elements(num_files)); add_benches(&mut group, num_files, gen_files); } } fn dir_heavy(c: &mut Criterion) { fn gen_files(dir: &Path, num_files: u64) { Generator::builder() .root_dir(dir.to_path_buf()) .num_files_with_ratio( NumFilesWithRatio::new( NonZeroU64::new(num_files).unwrap(), NonZeroU64::new(1).unwrap(), ) .unwrap(), ) .build() .generate(&mut io::sink().write_adapter()) .unwrap(); } let mut group = c.benchmark_group("dir_heavy"); group.plot_config(PlotConfiguration::default().summary_scale(AxisScale::Logarithmic)); for num_files in [10, 1_000, 100_000, 500_000] { group.sample_size(10); group.throughput(Throughput::Elements(num_files)); add_benches(&mut group, num_files, gen_files); } } fn file_heavy(c: &mut Criterion) { fn gen_files(dir: &Path, num_files: u64) { Generator::builder() .root_dir(dir.to_path_buf()) .num_files_with_ratio({ let num_files = NonZeroU64::new(num_files).unwrap(); NumFilesWithRatio::new(num_files, num_files).unwrap() }) .build() .generate(&mut io::sink().write_adapter()) .unwrap(); } let mut group = c.benchmark_group("file_heavy"); group.plot_config(PlotConfiguration::default().summary_scale(AxisScale::Logarithmic)); for num_files in [10, 1_000, 100_000] { group.sample_size(10); group.throughput(Throughput::Elements(num_files)); add_benches(&mut group, num_files, gen_files); } } fn files_only(c: &mut Criterion) { fn gen_files(dir: &Path, num_files: u64) { let mut file = dir.to_path_buf(); for i in 0..num_files { file.push(format!("{i}")); File::create(&file).unwrap(); file.pop(); } } let mut group = c.benchmark_group("files_only"); group.plot_config(PlotConfiguration::default().summary_scale(AxisScale::Logarithmic)); for num_files in [10, 1_000, 100_000] { group.sample_size(10); group.throughput(Throughput::Elements(num_files)); add_benches(&mut group, num_files, gen_files); } } fn deep_dirs(c: &mut Criterion) { fn gen_files(dir: &Path, num_files: u64) { Generator::builder() .root_dir(dir.to_path_buf()) .num_files_with_ratio( NumFilesWithRatio::new( NonZeroU64::new(num_files).unwrap(), NonZeroU64::new(10).unwrap(), ) .unwrap(), ) .max_depth(100) .build() .generate(&mut io::sink().write_adapter()) .unwrap(); } let mut group = c.benchmark_group("deep_dirs"); group.plot_config(PlotConfiguration::default().summary_scale(AxisScale::Logarithmic)); for num_files in [10, 1_000, 100_000, 1_000_000] { group.sample_size(10); group.throughput(Throughput::Elements(num_files)); add_benches(&mut group, num_files, gen_files); } } fn add_benches( group: &mut BenchmarkGroup<WallTime>, num_files: u64, gen_files: fn(&Path, num_files: u64), ) { group.bench_with_input( BenchmarkId::new("fuc_engine::remove_dir_all", num_files), &num_files, |b, &num_files| { b.iter_with_setup( || { let dir = tempdir().unwrap(); gen_files(dir.path(), num_files); dir }, |dir| { fuc_engine::remove_file(dir.path()).unwrap(); assert!(!dir.path().exists()); dir }, ); }, ); group.bench_with_input( BenchmarkId::new("rm_rayon::remove_dir_all", num_files), &num_files, |b, &num_files| { b.iter_with_setup( || { let dir = tempdir().unwrap(); gen_files(dir.path(), num_files); dir }, |dir| { rm_rayon::remove_dir_all(dir.path()).unwrap(); assert!(!dir.path().exists()); dir }, ); }, ); group.bench_with_input( BenchmarkId::new("rm_og_crappy::remove_dir_all", num_files), &num_files, |b, &num_files| { b.iter_with_setup( || { let dir = tempdir().unwrap(); gen_files(dir.path(), num_files); dir }, |dir| { rm_og_crappy::remove_dir_all(dir.path()).unwrap(); assert!(!dir.path().exists()); dir }, ); }, ); group.bench_with_input( BenchmarkId::new("fs::remove_dir_all", num_files), &num_files, |b, &num_files| { b.iter_with_setup( || { let dir = tempdir().unwrap(); gen_files(dir.path(), num_files); dir }, |dir| { fs::remove_dir_all(dir.path()).unwrap(); assert!(!dir.path().exists()); dir }, ); }, ); group.bench_with_input( BenchmarkId::new("remove_dir_all::remove_dir_all", num_files), &num_files, |b, &num_files| { b.iter_with_setup( || { let dir = tempdir().unwrap(); gen_files(dir.path(), num_files); dir }, |dir| { remove_dir_all::remove_dir_all(dir.path()).unwrap(); assert!(!dir.path().exists()); dir }, ); }, ); } criterion_group! { name = benches; config = Criterion::default().noise_threshold(0.02).warm_up_time(Duration::from_secs(1)); targets = uniform, dir_heavy, file_heavy, files_only, deep_dirs, } criterion_main!(benches); 07070100000056000081A40000000000000000000000016682237800000188000000000000000000000000000000000000002D00000000fuc-2.2.0/rmz/command-reference-short.goldenA zippy alternative to `rm`, a tool to remove files and directories Usage: rmz [OPTIONS] <FILES>... Arguments: <FILES>... The files and/or directories to be removed Options: -f, --force Ignore non-existent arguments --no-preserve-root Allow deletion of `/` -h, --help Print help (use `--help` for more detail) -V, --version Print version 07070100000057000081A40000000000000000000000016682237800000192000000000000000000000000000000000000002700000000fuc-2.2.0/rmz/command-reference.goldenA zippy alternative to `rm`, a tool to remove files and directories Usage: rmz [OPTIONS] <FILES>... Arguments: <FILES>... The files and/or directories to be removed Options: -f, --force Ignore non-existent arguments --no-preserve-root Allow deletion of `/` -h, --help Print help (use `-h` for a summary) -V, --version Print version 07070100000058000041ED0000000000000000000000026682237800000000000000000000000000000000000000000000001200000000fuc-2.2.0/rmz/src07070100000059000081A400000000000000000000000166822378000017D2000000000000000000000000000000000000001A00000000fuc-2.2.0/rmz/src/main.rsuse std::path::PathBuf; use clap::{ArgAction, Parser, ValueHint}; use error_stack::Report; use fuc_engine::{Error, RemoveOp}; /// A zippy alternative to `rm`, a tool to remove files and directories #[derive(Parser, Debug)] #[command(version, author = "Alex Saveau (@SUPERCILEX)")] #[command(infer_subcommands = true, infer_long_args = true)] #[command(disable_help_flag = true)] #[command(arg_required_else_help = true)] #[command(max_term_width = 100)] #[cfg_attr(test, command(help_expected = true))] struct Rmz { /// The files and/or directories to be removed #[arg(required = true)] #[arg(value_hint = ValueHint::AnyPath)] files: Vec<PathBuf>, /// Ignore non-existent arguments #[arg(short, long, default_value_t = false)] force: bool, /// Allow deletion of `/` #[arg(long = "no-preserve-root", default_value_t = true)] #[arg(action = ArgAction::SetFalse)] preserve_root: bool, #[arg(short, long, short_alias = '?', global = true)] #[arg(action = ArgAction::Help, help = "Print help (use `--help` for more detail)")] #[arg(long_help = "Print help (use `-h` for a summary)")] help: Option<bool>, } #[derive(thiserror::Error, Debug)] enum CliError { #[error("{0}")] Wrapper(String), } #[cfg(feature = "trace")] #[global_allocator] static GLOBAL: tracy_client::ProfiledAllocator<std::alloc::System> = tracy_client::ProfiledAllocator::new(std::alloc::System, 100); #[cfg(feature = "trace")] fn init_trace() { use tracing_subscriber::{ filter::LevelFilter, fmt::format::DefaultFields, layer::SubscriberExt, util::SubscriberInitExt, EnvFilter, }; #[derive(Default)] struct Config(DefaultFields); impl tracing_tracy::Config for Config { type Formatter = DefaultFields; fn formatter(&self) -> &Self::Formatter { &self.0 } fn stack_depth(&self, _: &tracing::Metadata<'_>) -> u16 { 32 } fn format_fields_in_zone_name(&self) -> bool { false } } tracing_subscriber::registry() .with(tracing_tracy::TracyLayer::new(Config::default())) .with( EnvFilter::builder() .with_default_directive(LevelFilter::TRACE.into()) .from_env_lossy(), ) .init(); } #[cfg(feature = "progress")] fn init_progress() { use std::time::Duration; use indicatif::{ProgressState, ProgressStyle}; use tracing::level_filters::LevelFilter; use tracing_indicatif::IndicatifLayer; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, EnvFilter}; let indicatif_layer = IndicatifLayer::new() .with_progress_style( ProgressStyle::with_template( "{color_start}{span_child_prefix}{span_fields} -- {span_name} {wide_msg} \ {elapsed_subsec}{color_end}", ) .unwrap() .with_key( "elapsed_subsec", |state: &ProgressState, writer: &mut dyn std::fmt::Write| { let seconds = state.elapsed().as_secs(); let sub_seconds = (state.elapsed().as_millis() % 1000) / 100; let _ = write!(writer, "{}.{}s", seconds, sub_seconds); }, ) .with_key( "color_start", |state: &ProgressState, writer: &mut dyn std::fmt::Write| { let elapsed = state.elapsed(); if elapsed > Duration::from_secs(8) { // Red let _ = write!(writer, "\x1b[{}m", 1 + 30); } else if elapsed > Duration::from_secs(4) { // Yellow let _ = write!(writer, "\x1b[{}m", 3 + 30); } }, ) .with_key( "color_end", |state: &ProgressState, writer: &mut dyn std::fmt::Write| { if state.elapsed() > Duration::from_secs(4) { let _ = write!(writer, "\x1b[0m"); } }, ), ) .with_span_child_prefix_symbol("↳ ") .with_span_child_prefix_indent(" "); tracing_subscriber::registry() .with(tracing_subscriber::fmt::layer().with_writer(indicatif_layer.get_stderr_writer())) .with(indicatif_layer) .with( EnvFilter::builder() .with_default_directive(LevelFilter::INFO.into()) .from_env_lossy(), ) .init(); } fn main() -> error_stack::Result<(), CliError> { #[cfg(not(debug_assertions))] error_stack::Report::install_debug_hook::<std::panic::Location>(|_, _| {}); #[cfg(feature = "trace")] init_trace(); #[cfg(feature = "progress")] init_progress(); let args = Rmz::parse(); remove(args).map_err(|e| { let wrapper = CliError::Wrapper(format!("{e}")); match e { Error::Io { error, context } => Report::from(error) .attach_printable(context) .change_context(wrapper), Error::NotFound { file: _ } => { Report::from(wrapper).attach_printable("Use --force to ignore.") } Error::PreserveRoot | Error::Join | Error::BadPath | Error::Internal => { Report::from(wrapper) } Error::AlreadyExists { file: _ } => unreachable!(), } }) } fn remove( Rmz { files, force, preserve_root, help: _, }: Rmz, ) -> Result<(), Error> { RemoveOp::builder() .files(files.into_iter()) .force(force) .preserve_root(preserve_root) .build() .run() } #[cfg(test)] mod cli_tests { use clap::CommandFactory; use super::*; #[test] fn verify_app() { Rmz::command().debug_assert(); } #[test] fn help_for_review() { supercilex_tests::help_for_review(Rmz::command()); } } 0707010000005A000041ED0000000000000000000000026682237800000000000000000000000000000000000000000000001400000000fuc-2.2.0/rmz/tests0707010000005B000081A40000000000000000000000016682237800000048000000000000000000000000000000000000001B00000000fuc-2.2.0/rmz/tests/api.rs#[test] fn readme() { trycmd::TestCases::new().case("README.md"); } 0707010000005C000081A40000000000000000000000016682237800000008000000000000000000000000000000000000001900000000fuc-2.2.0/rust-toolchainnightly 0707010000005D000041ED0000000000000000000000026682237800000000000000000000000000000000000000000000001000000000fuc-2.2.0/tests0707010000005E000081A4000000000000000000000001668223780000006B000000000000000000000000000000000000001700000000fuc-2.2.0/tests/api.rs#[test] fn fmt() { supercilex_tests::fmt(); } #[test] fn clippy() { supercilex_tests::clippy(); } 07070100000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000B00000000TRAILER!!!496 blocks
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor