diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..31b4342 --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,20 @@ +# From https://github.com/BurntSushi/ripgrep/blob/master/.cargo/config.toml + +# Do the same for MUSL targets. At the time of writing (2023-10-23), this is +# the default. But the plan is for the default to change to dynamic linking. +# The whole point of MUSL with respect to ripgrep is to create a fully +# statically linked executable. +# +# See: https://github.com/rust-lang/compiler-team/issues/422 +# See: https://github.com/rust-lang/compiler-team/issues/422#issuecomment-812135847 +[target.x86_64-unknown-linux-musl] +rustflags = [ + "-C", "target-feature=+crt-static", + "-C", "link-self-contained=yes", +] + +[target.aarch64-unknown-linux-musl] +rustflags = [ + "-C", "target-feature=+crt-static", + "-C", "link-self-contained=yes", +] diff --git a/.copier-answers.yml b/.copier-answers.yml new file mode 100644 index 0000000..5d0d818 --- /dev/null +++ b/.copier-answers.yml @@ -0,0 +1,6 @@ +# Changes here will be overwritten by Copier +_commit: v1.7.8 +_src_path: https://github.com/DeveloperC286/template +project_name: unreferenced_files +project_type: rust +uses_git: false diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..3550a30 --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use flake diff --git a/.github/renovate.json5 b/.github/renovate.json5 new file mode 100644 index 0000000..945c8ab --- /dev/null +++ b/.github/renovate.json5 @@ -0,0 +1,36 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": [ + "config:best-practices" + ], + "automerge": true, + "github-actions": { + "enabled": false + }, + "nix": { + "enabled": true, + "lockFileMaintenance": { + "enabled": true, + "commitMessageAction": "update", + "commitMessageTopic": "Nix flake lock" + } + }, + "customManagers": [ + { + "customType": "regex", + "managerFilePatterns": [ + "/(^|/|\\.)Dockerfile$/", + "/(^|/)Dockerfile[^/]*$/" + ], + "matchStringsStrategy": "recursive", + "matchStrings": [ + "FROM\\s*.*?alpine[.:]?(?\\d+)\\.(?\\d+)(?:\\n|.)*", + "apk\\s+add\\s+(?:\\n|.)*?[^\\\\]\\n", + "(?[^\\s=~]+)~?=(?[^\\s=]+)" + ], + "datasourceTemplate": "repology", + "depNameTemplate": "alpine_{{alpineMajor}}_{{alpineMinor}}/{{name}}", + "versioningTemplate": "loose" + } + ] +} diff --git a/.github/workflows/continuous-delivery.yml b/.github/workflows/continuous-delivery.yml new file mode 100644 index 0000000..cedcc7a --- /dev/null +++ b/.github/workflows/continuous-delivery.yml @@ -0,0 +1,89 @@ +name: Continuous Delivery (CD) + +on: + release: + types: [published] + +# https://docs.github.com/en/actions/using-jobs/assigning-permissions-to-jobs +permissions: + contents: write + packages: write + +jobs: + publish-binary: + name: Publish Binary + runs-on: ${{ matrix.architecture }} + strategy: + matrix: + architecture: [ubuntu-24.04, ubuntu-24.04-arm] + steps: + - name: Checkout code. + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - name: Setup Nix. + uses: cachix/install-nix-action@ab739621df7a23f52766f9ccc97f38da6b7af14f # v31.10.5 + - name: Publish binary. + run: nix develop -c make publish-binary RELEASE="${GITHUB_REF_NAME}" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} # This token is provided by GitHub Actions. + + publish-crate: + name: Publish Crate + runs-on: ubuntu-24.04 + steps: + - name: Checkout code. + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - name: Setup Nix. + uses: cachix/install-nix-action@ab739621df7a23f52766f9ccc97f38da6b7af14f # v31.10.5 + - name: Publish crate. + run: nix develop -c make publish-crate + env: + CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} + + publish-docker-image: + name: Publish Docker Image (${{ matrix.platform }}) + runs-on: ${{ matrix.runner }} + needs: [publish-binary] + strategy: + matrix: + include: + - platform: linux/amd64 + runner: ubuntu-24.04 + target: x86_64-unknown-linux-musl + suffix: amd64 + - platform: linux/arm64 + runner: ubuntu-24.04-arm + target: aarch64-unknown-linux-musl + suffix: arm64 + steps: + - name: Checkout code. + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0 + - name: Login to GitHub Container Registry + uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Publish Docker Image + run: make publish-docker-image RELEASE="${GITHUB_REF_NAME}" PLATFORM="${{ matrix.platform }}" TARGET="${{ matrix.target }}" SUFFIX="${{ matrix.suffix }}" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + publish-docker-manifest: + name: Publish Docker Manifest + runs-on: ubuntu-24.04 + needs: [publish-docker-image] + steps: + - name: Checkout code. + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0 + - name: Login to GitHub Container Registry + uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Publish Docker Manifest + run: make publish-docker-manifest RELEASE="${GITHUB_REF_NAME}" diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml new file mode 100644 index 0000000..b25d99a --- /dev/null +++ b/.github/workflows/continuous-integration.yml @@ -0,0 +1,107 @@ +name: Continuous Integration (CI) + +on: pull_request + +permissions: + contents: read + +jobs: + permissions: + name: Permissions + runs-on: ${{ matrix.architecture }} + strategy: + matrix: + architecture: [ubuntu-24.04, ubuntu-24.04-arm] + language: [shell] + steps: + - name: Checkout code. + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - name: Check permissions. + run: make check-${{ matrix.language }}-permissions + + formatting: + name: Formatting + runs-on: ${{ matrix.architecture }} + strategy: + matrix: + architecture: [ubuntu-24.04, ubuntu-24.04-arm] + language: [rust, shell, python] + steps: + - name: Checkout code. + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - name: Setup Nix. + uses: cachix/install-nix-action@ab739621df7a23f52766f9ccc97f38da6b7af14f # v31.10.5 + - name: Check formatting. + run: nix develop -c make check-${{ matrix.language }}-formatting + + linting: + name: Linting + runs-on: ${{ matrix.architecture }} + strategy: + matrix: + architecture: [ubuntu-24.04, ubuntu-24.04-arm] + language: [rust, shell, python] + steps: + - name: Checkout code. + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - name: Setup Nix. + uses: cachix/install-nix-action@ab739621df7a23f52766f9ccc97f38da6b7af14f # v31.10.5 + - name: Check linting. + run: nix develop -c make check-${{ matrix.language }}-linting + + dependencies: + name: Dependencies + runs-on: ${{ matrix.architecture }} + strategy: + matrix: + architecture: [ubuntu-24.04, ubuntu-24.04-arm] + language: [rust] + steps: + - name: Checkout code. + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - name: Setup Nix. + uses: cachix/install-nix-action@ab739621df7a23f52766f9ccc97f38da6b7af14f # v31.10.5 + - name: Check dependencies. + run: nix develop -c make check-${{ matrix.language }}-dependencies + + compile: + name: Compile + runs-on: ${{ matrix.architecture }} + strategy: + matrix: + architecture: [ubuntu-24.04, ubuntu-24.04-arm] + steps: + - name: Checkout code. + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - name: Setup Nix. + uses: cachix/install-nix-action@ab739621df7a23f52766f9ccc97f38da6b7af14f # v31.10.5 + - name: Compile. + run: nix develop -c make compile + + unit-test: + name: Unit Test + runs-on: ${{ matrix.architecture }} + strategy: + matrix: + architecture: [ubuntu-24.04, ubuntu-24.04-arm] + steps: + - name: Checkout code. + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - name: Setup Nix. + uses: cachix/install-nix-action@ab739621df7a23f52766f9ccc97f38da6b7af14f # v31.10.5 + - name: Unit test. + run: nix develop -c make unit-test + + end-to-end-test: + name: End to End Test + runs-on: ${{ matrix.architecture }} + strategy: + matrix: + architecture: [ubuntu-24.04, ubuntu-24.04-arm] + steps: + - name: Checkout code. + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - name: Setup Nix. + uses: cachix/install-nix-action@ab739621df7a23f52766f9ccc97f38da6b7af14f # v31.10.5 + - name: End to End test. + run: nix develop -c make end-to-end-test diff --git a/.github/workflows/conventional-commits.yml b/.github/workflows/conventional-commits.yml new file mode 100644 index 0000000..4906890 --- /dev/null +++ b/.github/workflows/conventional-commits.yml @@ -0,0 +1,26 @@ +name: Conventional Commits + +on: + pull_request: + types: [opened, edited, synchronize, reopened] + +permissions: + contents: read + +jobs: + linting: + name: Linting + runs-on: ubuntu-24.04 + container: + image: ghcr.io/developerc286/conventional_commits_linter:0.17.1@sha256:f1b947937ee884ba7f886d04939cd4858f9aeafb50dcf94925a516c50e43021b + steps: + - name: Checkout code. + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + ref: ${{ github.event.pull_request.head.sha }} + fetch-depth: 0 + - name: Check Conventional Commits linting. + env: + PR_TITLE: ${{ github.event.pull_request.title }} + PR_BODY: ${{ github.event.pull_request.body }} + run: printf '%s\n\n%s' "$PR_TITLE" "$PR_BODY" | conventional_commits_linter - diff --git a/.github/workflows/dogfood.yml b/.github/workflows/dogfood.yml new file mode 100644 index 0000000..2fad128 --- /dev/null +++ b/.github/workflows/dogfood.yml @@ -0,0 +1,21 @@ +name: Dogfood + +on: pull_request + +permissions: + contents: read + +jobs: + docker: + name: Docker + runs-on: ${{ matrix.architecture }} + strategy: + matrix: + architecture: [ubuntu-24.04, ubuntu-24.04-arm] + steps: + - name: Checkout code. + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + - name: Setup Nix. + uses: cachix/install-nix-action@4e002c8ec80594ecd40e759629461e26c8abed15 # v31.9.0 + - name: Dogfooding Docker. + run: nix develop -c make dogfood-docker diff --git a/.github/workflows/git-history.yml b/.github/workflows/git-history.yml new file mode 100644 index 0000000..8aaedad --- /dev/null +++ b/.github/workflows/git-history.yml @@ -0,0 +1,21 @@ +name: Git History + +on: pull_request + +permissions: + contents: read + +jobs: + clean: + name: Clean + runs-on: ubuntu-24.04 + container: + image: ghcr.io/developerc286/clean_git_history:1.2.0@sha256:bdbd6ca09529f44010583fd89528a96961f7acb12bdd7baa975b0811032a647e + steps: + - name: Checkout code. + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + ref: ${{ github.event.pull_request.head.sha }} + fetch-depth: 0 + - name: Check clean Git history. + run: clean_git_history "origin/${{ github.base_ref }}" diff --git a/.github/workflows/github-actions-workflows.yml b/.github/workflows/github-actions-workflows.yml new file mode 100644 index 0000000..75a8c16 --- /dev/null +++ b/.github/workflows/github-actions-workflows.yml @@ -0,0 +1,35 @@ +name: GitHub Actions Workflows + +on: pull_request + +permissions: + contents: read + +jobs: + formatting: + name: Formatting + runs-on: ${{ matrix.architecture }} + strategy: + matrix: + architecture: [ubuntu-24.04, ubuntu-24.04-arm] + steps: + - name: Checkout code. + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - name: Setup Nix. + uses: cachix/install-nix-action@ab739621df7a23f52766f9ccc97f38da6b7af14f # v31.10.5 + - name: Check formatting. + run: nix develop -c make check-yaml-formatting + + linting: + name: Linting + runs-on: ${{ matrix.architecture }} + strategy: + matrix: + architecture: [ubuntu-24.04, ubuntu-24.04-arm] + steps: + - name: Checkout code. + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - name: Setup Nix. + uses: cachix/install-nix-action@ab739621df7a23f52766f9ccc97f38da6b7af14f # v31.10.5 + - name: Check GitHub Actions workflows linting. + run: nix develop -c make check-github-actions-workflows-linting diff --git a/.github/workflows/mirroring.yml b/.github/workflows/mirroring.yml new file mode 100644 index 0000000..b816fa2 --- /dev/null +++ b/.github/workflows/mirroring.yml @@ -0,0 +1,19 @@ +name: Mirroring + +on: [push, delete] + +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }} + +jobs: + gitlab: + name: GitLab + runs-on: ubuntu-24.04 + steps: + - name: Checkout code. + run: git clone --mirror "https://github.com/${GITHUB_REPOSITORY}.git" "${GITHUB_WORKSPACE}" + - name: Mirroring. + run: git push --mirror "https://oauth2:${{ secrets.GITLAB_PERSONAL_ACCESS_TOKEN }}@gitlab.com/DeveloperC/${{ github.event.repository.name }}" diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml new file mode 100644 index 0000000..8384c37 --- /dev/null +++ b/.github/workflows/release-please.yml @@ -0,0 +1,19 @@ +name: Continuous Delivery (CD) + +on: + push: + branches: + - main + +# https://docs.github.com/en/actions/using-jobs/assigning-permissions-to-jobs +permissions: + contents: write + pull-requests: write + +jobs: + release-please: + runs-on: ubuntu-24.04 + steps: + - uses: googleapis/release-please-action@45996ed1f6d02564a971a2fa1b5860e934307cf7 # v5.0 + with: + token: ${{ secrets.MY_RELEASE_PLEASE_TOKEN }} diff --git a/.gitignore b/.gitignore index 44cb8de..2b4217c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,13 @@ +# Rust .gitignore # Generated by Cargo -# will have compiled files and executables -debug/ -target/ +debug +target -# These are backup files generated by rustfmt +# Generated by rustfmt **/*.rs.bk -end-to-end-tests/features/steps/__pycache__/ +# Generated by proptest +proptest-regressions + +# Generated by direnv & Nix. +.direnv diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml deleted file mode 100644 index 4c81f3d..0000000 --- a/.gitlab-ci.yml +++ /dev/null @@ -1,176 +0,0 @@ -stages: - - conventional-commits-linting - - conventional-commits-next-version-checking - - formatting - - linting - - compiling - - unit-testing - - end-to-end-testing - - releasing - - crates-io-publishing - - release-binary-compiling - - -conventional-commits-linting: - stage: conventional-commits-linting - image: rust - before_script: - - cargo install conventional_commits_linter - script: - - COMMON_ANCESTOR_COMMIT=`git merge-base origin/$CI_MERGE_REQUEST_SOURCE_BRANCH_NAME origin/$CI_MERGE_REQUEST_TARGET_BRANCH_NAME` - # Lint all the commits in the branch. - - /usr/local/cargo/bin/conventional_commits_linter --from-commit-hash $COMMON_ANCESTOR_COMMIT --allow-angular-type-only - rules: - - if: $CI_MERGE_REQUEST_ID - - -conventional-commits-next-version-checking: - stage: conventional-commits-next-version-checking - image: rust - before_script: - - cargo install conventional_commits_next_version - script: - # Get current version and latest tag. - - CURRENT_VERSION=`grep '^version = "[0-9][0-9]*.[0-9][0-9]*.[0-9][0-9]*"$' Cargo.toml | cut -d '"' -f 2` - # Get latest tag. - - LATEST_TAG=`git describe --tags --abbrev=0` - # Check latest tag is in semantic versioning. - - echo $LATEST_TAG | grep "^[0-9][0-9]*.[0-9][0-9]*.[0-9][0-9]*$" - # Check current vs expected. - - /usr/local/cargo/bin/conventional_commits_next_version --batch-commits --from-reference $LATEST_TAG --from-version $LATEST_TAG --current-version $CURRENT_VERSION - rules: - - if: $CI_MERGE_REQUEST_ID - - -unreferenced-files-formatting: - stage: formatting - image: rust - before_script: - - rustup component add rustfmt - script: - - cargo fmt --all -- --check - rules: - - if: $CI_MERGE_REQUEST_ID - - -end-to-end-tests-formatting: - stage: formatting - image: python:3-slim - before_script: - - pip3 install autopep8 - script: - - for i in $( du -a ./end-to-end-tests/ | awk '{print $2}' | grep -i "[.]py$" ); do - - cp $i temp.txt - - autopep8 --in-place --aggressive --aggressive $i - - cmp $i temp.txt - - done - rules: - - if: $CI_MERGE_REQUEST_ID - - -linting: - stage: linting - image: rust - before_script: - - rustup component add clippy - script: - - cargo clippy --verbose --workspace --all-targets -- -D warnings - - cargo clippy --verbose --workspace --all-targets --all-features -- -D warnings - - cargo clippy --verbose --workspace --all-targets --no-default-features -- -D warnings - rules: - - if: $CI_MERGE_REQUEST_ID - - -compiling: - stage: compiling - image: rust - script: - - cargo build --verbose --workspace - - cargo build --verbose --workspace --all-features - - cargo build --verbose --workspace --no-default-features - rules: - - if: $CI_MERGE_REQUEST_ID - - if: $CI_COMMIT_BRANCH == "master" - - -unit-testing: - stage: unit-testing - image: rust - script: - - cargo test --verbose --workspace - - cargo test --verbose --workspace --all-features - - cargo test --verbose --workspace --no-default-features - rules: - - if: $CI_MERGE_REQUEST_ID - - if: $CI_COMMIT_BRANCH == "master" - - -end-to-end-testing: - stage: end-to-end-testing - image: python:3-slim - before_script: - - apt update - - apt install git cargo -y - - cargo build - - cd end-to-end-tests/ - - pip3 install -r requirements.txt - script: - - behave - rules: - - if: $CI_MERGE_REQUEST_ID - - if: $CI_COMMIT_BRANCH == "master" - - -releasing: - stage: releasing - image: registry.gitlab.com/gitlab-org/release-cli - before_script: - - apk add git - script: - - CURRENT_VERSION=`grep '^version = "[0-9][0-9]*.[0-9][0-9]*.[0-9][0-9]*"$' Cargo.toml | cut -d '"' -f 2` - # If the tag already exist then exit. - - git tag -l | grep "^${CURRENT_VERSION}$" && exit 0 - # Get latest tag. - - LATEST_TAG=`git describe --tags | cut -d '-' -f 1` - - LATEST_TAG_HASH=`git rev-parse $LATEST_TAG` - # Check latest tag is in semantic versioning. - - echo $LATEST_TAG | grep "^[0-9][0-9]*.[0-9][0-9]*.[0-9][0-9]*$" - # Download clog. - - wget https://github.com/clog-tool/clog-cli/releases/download/v0.9.3/clog-v0.9.3-x86_64-unknown-linux-musl.tar.gz - - tar xvf clog-v0.9.3-x86_64-unknown-linux-musl.tar.gz - # Generate the release description. - - RELEASE_DESCRIPTION=`./clog --from $LATEST_TAG_HASH --link-style Gitlab --subtitle $CURRENT_VERSION | tail -n +2` - # Create the new release. - - release-cli create - --name $CURRENT_VERSION - --description "$RELEASE_DESCRIPTION" - --tag-name $CURRENT_VERSION - --ref $CI_COMMIT_SHA - --assets-link '{"name":"x86_64-linux-musl-binary.zip","url":"https://gitlab.com/DeveloperC/unreferenced_files/-/jobs/artifacts/'$CURRENT_VERSION'/download?job=release-binary-compiling-x86_64-linux-musl"}' - rules: - - if: $CI_COMMIT_BRANCH == "master" - - -crates-io-publishing: - stage: crates-io-publishing - image: rust - script: - - cargo publish --token $GITLAB_CRATES_IO_TOKEN - rules: - - if: $CI_COMMIT_TAG - - -release-binary-compiling-x86_64-linux-musl: - stage: release-binary-compiling - image: rust:alpine - before_script: - - apk add --no-cache musl-dev - script: - - cargo build --release --target x86_64-unknown-linux-musl - - mv target/x86_64-unknown-linux-musl/release/unreferenced_files unreferenced_files - - strip unreferenced_files - artifacts: - paths: - - unreferenced_files - rules: - - if: $CI_COMMIT_TAG diff --git a/.release-please-manifest.json b/.release-please-manifest.json new file mode 100644 index 0000000..33bf7bf --- /dev/null +++ b/.release-please-manifest.json @@ -0,0 +1 @@ +{".":"2.2.0"} diff --git a/.yamlfmt b/.yamlfmt new file mode 100644 index 0000000..e9d24f9 --- /dev/null +++ b/.yamlfmt @@ -0,0 +1,3 @@ +formatter: + type: basic + retain_line_breaks: true diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..1d63056 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,333 @@ +# Changelog + +## 2.2.0 (2026-01-04) + +## What's Changed +* build: simplifying Makefile by @DeveloperC286 in https://github.com/DeveloperC286/unreferenced_files/pull/198 +* chore(deps): update mvdan/shfmt docker tag to v3.12.0 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/200 +* chore(deps): update dependency alpine_3_21/git to v2.47.3-r0 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/201 +* fix(deps): update rust crate clap to v4.5.41 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/202 +* feat: adding verbose CLI option by @DeveloperC286 in https://github.com/DeveloperC286/unreferenced_files/pull/203 +* docs: condensed/simplified README.md by @DeveloperC286 in https://github.com/DeveloperC286/unreferenced_files/pull/204 +* chore(deps): update alpine:3.22 docker digest to 4bcff63 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/205 +* chore(deps): update python:3.13.5-alpine3.21 docker digest to 6a5f50a by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/206 +* chore(deps): update python:3.13.5-alpine3.21 docker digest to fd94400 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/207 +* chore(deps): update rust:1.88.0-alpine3.21 docker digest to 63f574f by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/208 +* chore(deps): update python:3.13.5-alpine3.21 docker digest to 716e13a by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/209 +* chore(deps): update rust:1.88.0-alpine3.21 docker digest to b7a47e9 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/210 +* chore(deps): update alpine docker tag to v3.22.1 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/211 +* chore(deps): update python:3.13.5-alpine3.21 docker digest to 763eee4 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/212 +* chore(deps): update rust:1.88.0-alpine3.21 docker digest to 54e937b by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/213 +* fix(deps): update rust crate clap to v4.5.42 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/214 +* chore(deps): update dependency behave to v1.3.0 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/215 +* chore(deps): update docker/login-action digest to 184bdaa by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/216 +* fix(deps): update rust crate clap to v4.5.43 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/217 +* chore(deps): update python docker tag to v3.13.6 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/218 +* chore(deps): update python:3.13.6-alpine3.21 docker digest to f5f2574 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/219 +* chore(deps): update dependency alpine_3_22/github-cli to v2.72.0-r2 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/220 +* chore(deps): update rust docker tag to v1.89.0 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/221 +* chore(deps): update actions/checkout action to v4.3.0 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/222 +* chore(deps): update actions/checkout action to v5 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/223 +* chore(deps): update dependency behave to v1.3.1 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/224 +* fix(deps): update rust crate clap to v4.5.44 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/225 +* chore(deps): update dependency parse-type to v0.6.6 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/227 +* fix(deps): update rust crate anyhow to v1.0.99 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/228 +* fix(deps): update rust crate clap to v4.5.45 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/229 +* chore(deps): update python:3.13.6-alpine3.21 docker digest to 5bcbb2a by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/230 +* chore(deps): update python:3.13.6-alpine3.21 docker digest to 52153b8 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/231 +* chore(deps): update python:3.13.6-alpine3.21 docker digest to 2af1351 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/232 +* chore(deps): update python:3.13.6-alpine3.21 docker digest to 4358f4b by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/233 +* chore(deps): update python docker tag to v3.13.7 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/234 +* chore(deps): update python:3.13.7-alpine3.21 docker digest to 8f70fe3 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/235 +* chore(deps): update python:3.13.7-alpine3.21 docker digest to 0c3d4f2 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/236 +* chore(deps): update googleapis/release-please-action action to v4.3.0 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/237 +* fix(deps): update rust crate regex to v1.11.2 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/238 +* fix(deps): update rust crate clap to v4.5.46 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/239 +* chore(deps): update dependency behave to v1.3.2 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/240 +* chore(deps): update dependency behave to v1.3.3 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/241 +* chore(deps): update rust crate insta to v1.43.2 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/242 +* fix(deps): update rust crate clap to v4.5.47 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/243 +* fix(deps): update rust crate log to v0.4.28 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/244 +* chore(deps): update dependency alpine_3_22/github-cli to v2.72.0-r3 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/245 +* chore(deps): update ghcr.io/developerc286/clean_git_history docker tag to v1.1.0 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/246 +* chore(deps): update rust docker tag to v1.90.0 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/248 +* fix(deps): update rust crate anyhow to v1.0.100 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/249 +* fix(deps): update rust crate clap to v4.5.48 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/250 +* ci: adding Claude Code workflows by @DeveloperC286 in https://github.com/DeveloperC286/unreferenced_files/pull/251 +* chore(deps): update ghcr.io/developerc286/conventional_commits_linter docker tag to v0.16.0 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/247 +* chore(deps): update anthropics/claude-code-action digest to 426380f by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/252 +* chore(deps): update docker/login-action digest to 5e57cd1 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/253 +* chore(deps): update anthropics/claude-code-action digest to 90d189f by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/254 +* chore(deps): update anthropics/claude-code-action digest to ac1a320 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/255 +* chore(deps): update python docker tag to v3.14.0 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/256 +* chore(deps): update python:3.14.0-alpine3.21 docker digest to c2410fb by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/258 +* chore(deps): update rust:1.90.0-alpine3.21 docker digest to 1b3ecdc by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/259 +* chore(deps): update alpine:3.22 docker digest to 4b7ce07 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/257 +* chore(deps): update rust crate regex to v1.11.3 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/263 +* chore(deps): update python:3.14.0-alpine3.21 docker digest to 814983b by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/261 +* chore(deps): update rust:1.90.0-alpine3.21 docker digest to 3757b14 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/262 +* chore(deps): update alpine docker tag to v3.22.2 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/260 +* chore(deps): update anthropics/claude-code-action digest to 777ffcb by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/264 +* chore(deps): update rust crate regex to v1.12.1 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/265 +* chore(deps): update python:3.14.0-alpine3.21 docker digest to f1ac9e0 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/266 +* chore(deps): update rhysd/actionlint docker tag to v1.7.8 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/267 +* chore(deps): update dependency alpine_3_22/github-cli to v2.72.0-r4 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/268 +* chore(deps): update rust crate clap to v4.5.49 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/269 +* chore(deps): update rust crate regex to v1.12.2 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/270 +* chore(deps): update ghcr.io/google/yamlfmt docker tag to v0.18.0 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/271 +* chore(deps): update anthropics/claude-code-action digest to e8bad57 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/272 +* ci: Claude Code only reviewing owner by @DeveloperC286 in https://github.com/DeveloperC286/unreferenced_files/pull/273 +* chore(deps): update ghcr.io/google/yamlfmt docker tag to v0.18.1 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/274 +* chore(deps): update ghcr.io/google/yamlfmt docker tag to v0.19.0 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/275 +* chore(deps): update rust crate clap to v4.5.50 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/276 +* chore(deps): update ghcr.io/google/yamlfmt docker tag to v0.20.0 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/277 +* chore(deps): update anthropics/claude-code-action digest to f30f5ee by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/278 +* chore(deps): update googleapis/release-please-action action to v4.4.0 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/279 +* chore(deps): update anthropics/claude-code-action digest to f4d737a by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/280 +* chore(deps): update rust crate clap to v4.5.51 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/281 +* chore(deps): update anthropics/claude-code-action digest to 8a1c437 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/282 +* chore(deps): update rust docker tag to v1.91.0 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/283 +* chore(deps): update rust docker tag to v1.91.1 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/284 +* chore(deps): update ghcr.io/developerc286/clean_git_history docker tag to v1.1.1 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/285 +* chore(deps): update ghcr.io/developerc286/conventional_commits_linter docker tag to v0.16.1 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/286 +* ci: skip Claude Code review on draft PRs by @DeveloperC286 in https://github.com/DeveloperC286/unreferenced_files/pull/287 +* chore(deps): update actions/checkout digest to 93cb6ef by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/288 +* chore(deps): update actions/checkout action to v5.0.1 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/289 +* chore(deps): update anthropics/claude-code-action digest to 906bd89 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/290 +* chore(deps): update rust crate clap to v4.5.52 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/291 +* chore(deps): update anthropics/claude-code-action digest to 6902c22 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/292 +* chore(deps): update rust crate clap to v4.5.53 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/293 +* chore(deps): update rust crate insta to v1.44.0 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/294 +* chore(deps): update actions/checkout action to v6 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/295 +* chore(deps): update rust crate insta to v1.44.1 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/296 +* chore(deps): update rhysd/actionlint docker tag to v1.7.9 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/297 +* chore(deps): update anthropics/claude-code-action digest to f9b2917 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/298 +* chore(deps): update anthropics/claude-code-action digest to 798cf09 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/299 +* chore(deps): update anthropics/claude-code-action digest to a7e4c51 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/300 +* chore(deps): update rust crate insta to v1.44.2 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/301 +* chore(deps): update rust crate insta to v1.44.3 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/302 +* chore(deps): update anthropics/claude-code-action digest to 6337623 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/303 +* chore(deps): update actions/checkout digest to 8e8c483 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/304 +* chore(deps): update actions/checkout action to v6.0.1 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/305 +* chore(deps): update rust crate log to v0.4.29 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/307 +* chore(deps): update python docker tag to v3.14.1 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/306 +* chore(deps): update ghcr.io/developerc286/conventional_commits_linter docker tag to v0.17.0 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/308 +* chore(deps): update python:3.14.1-alpine3.21 docker digest to fd8407c by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/309 +* chore(deps): update dependency alpine_3_22/github-cli to v2.72.0-r5 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/310 +* chore(deps): update alpine docker tag by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/311 +* chore(deps): update rust:1.91.1-alpine3.21 docker digest to 32817dd by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/312 +* chore(deps): update dependency alpine_3_23/github-cli to v2.83.0-r0 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/313 +* chore(deps): update dependency alpine_3_23/github-cli to v2.83.0-r1 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/314 +* chore(deps): update anthropics/claude-code-action digest to f0c8eb2 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/315 +* chore(deps): update rust docker tag to v1.92.0 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/316 +* chore(deps): update anthropics/claude-code-action digest to 9acae26 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/317 +* chore(deps): update anthropics/claude-code-action digest to d7b6d50 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/318 +* chore(deps): update alpine:3.23 docker digest to be171b5 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/319 +* chore(deps): update anthropics/claude-code-action digest to 0d19335 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/320 +* chore(deps): update alpine:3.23 docker digest to 865b95f by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/321 +* chore(deps): update alpine docker tag to v3.23.2 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/322 +* chore(deps): update rust crate insta to v1.45.0 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/323 +* chore(deps): update docker/setup-buildx-action digest to 8d2750c by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/324 +* chore(deps): update anthropics/claude-code-action digest to 7145c3e by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/325 +* chore(deps): update ghcr.io/developerc286/clean_git_history docker tag to v1.1.5 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/326 +* ci: deleting Claude workflows by @DeveloperC286 in https://github.com/DeveloperC286/unreferenced_files/pull/327 +* chore(deps): update rust crate insta to v1.45.1 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/328 +* chore(deps): update rhysd/actionlint docker tag to v1.7.10 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/329 +* chore(deps): update rust crate clap to v4.5.54 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/330 +* chore(deps): update rust crate insta to v1.46.0 by @renovate[bot] in https://github.com/DeveloperC286/unreferenced_files/pull/332 +* ci: converting from Docker -> Nix by @DeveloperC286 in https://github.com/DeveloperC286/unreferenced_files/pull/331 + + +**Full Changelog**: https://github.com/DeveloperC286/unreferenced_files/compare/v2.1.1...v2.2.0 + +## 2.1.1 (2025-07-01) + +## What's Changed +* fix(deps): update rust crate regex to v1.11.1 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/42 +* fix(deps): update rust crate anyhow to v1.0.93 - autoclosed by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/41 +* chore(deps): update rust crate insta to v1.41.1 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/43 +* ci: Alpine migration for pinning/updating all dependencies by @DeveloperC286 in https://github.com/DeveloperC286/unreferenced_files/pull/46 +* ci: adding Renovate GitHub Workflow Earthly updating by @DeveloperC286 in https://github.com/DeveloperC286/unreferenced_files/pull/52 +* chore(deps): update dependency alpine_3_20/python3 to v3.12.8-r1 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/57 +* chore(deps): update earthly/earthly docker tag to v0.8.15 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/53 +* ci: replacing missed Earthly download with action by @DeveloperC286 in https://github.com/DeveloperC286/unreferenced_files/pull/59 +* build: converting to Rust's Alpine image by @DeveloperC286 in https://github.com/DeveloperC286/unreferenced_files/pull/60 +* chore(deps): update dependency developerc286/conventional_commits_linter to v0.14.3 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/54 +* build: improved continuous delivery target naming by @DeveloperC286 in https://github.com/DeveloperC286/unreferenced_files/pull/62 +* build: adding cargo --locked and removing --workspace by @DeveloperC286 in https://github.com/DeveloperC286/unreferenced_files/pull/63 +* ci: removing Alpine Renovate version updating by @DeveloperC286 in https://github.com/DeveloperC286/unreferenced_files/pull/64 +* ci: converting to publishing .tar.gz by @DeveloperC286 in https://github.com/DeveloperC286/unreferenced_files/pull/65 +* chore(deps): update dependency alpine_3_20/git to v2.45.3-r0 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/68 +* ci: pinning actions/checkout at v4.2.2 by @DeveloperC286 in https://github.com/DeveloperC286/unreferenced_files/pull/67 +* ci: pinning earthly/actions-setup at v1.0.13 by @DeveloperC286 in https://github.com/DeveloperC286/unreferenced_files/pull/69 +* ci: pinning googleapis/release-please-action at v4.1.3 by @DeveloperC286 in https://github.com/DeveloperC286/unreferenced_files/pull/70 +* ci: using Earthly GitHub releases by @DeveloperC286 in https://github.com/DeveloperC286/unreferenced_files/pull/71 +* ci: Renovate automerge PRs by @DeveloperC286 in https://github.com/DeveloperC286/unreferenced_files/pull/72 +* chore(deps): pin dependencies by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/47 +* fix(deps): update rust crate anyhow to v1.0.95 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/55 +* fix(deps): update rust crate clap to v4.5.27 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/45 +* fix(deps): update rust crate log to v0.4.25 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/73 +* chore(deps): update dependency mvdan/sh to v3.10.0 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/49 +* chore(deps): update dependency google/yamlfmt to v0.15.0 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/48 +* chore(deps): update dependency rhysd/actionlint to v1.7.7 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/50 +* chore(deps): update dependency six to v1.17.0 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/56 +* chore(deps): update golang docker tag to v1.23.5 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/51 +* chore(deps): update rust crate insta to v1.42.1 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/66 +* chore(deps): update rust docker tag to v1.84.1 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/61 +* fix(deps): update rust crate clap to v4.5.28 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/74 +* chore(deps): update ubuntu:24.04 docker digest to 7229784 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/75 +* chore(deps): update golang:1.23.5 docker digest to e213430 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/76 +* chore(deps): update golang docker tag to v1.23.6 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/77 +* chore(deps): update golang:1.23.6 docker digest to b2a6f50 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/78 +* chore(deps): update golang:1.23.6 docker digest to 958bd2e by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/79 +* chore(deps): update golang:1.23.6 docker digest to 9271129 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/80 +* chore(deps): update dependency alpine_3_20/python3 to v3.12.9-r0 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/81 +* chore(deps): pin dependencies by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/82 +* chore(deps): update rust crate insta to v1.42.1 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/83 +* chore(deps): update rust crate lazy_static to v1.5.0 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/84 +* chore(deps): update dependency google/yamlfmt to v0.16.0 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/85 +* fix(deps): update rust crate clap to v4.5.29 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/86 +* chore(deps): update golang docker tag to v1.24.0 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/87 +* chore(deps): update golang:1.24.0 docker digest to 2b1cbf2 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/88 +* chore(deps): update dependency alpine_3_20/musl-dev to v1.2.5-r1 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/89 +* chore(deps): update rust:1.84.1-alpine3.20 docker digest to ac5caa3 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/90 +* fix(deps): update rust crate clap to v4.5.30 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/91 +* fix(deps): update rust crate anyhow to v1.0.96 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/92 +* fix(deps): update rust crate log to v0.4.26 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/94 +* chore(deps): update rust docker tag to v1.85.0 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/93 +* fix(deps): update rust crate clap to v4.5.31 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/95 +* chore(deps): update golang:1.24.0 docker digest to 5255fad by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/96 +* chore(deps): update golang:1.24.0 docker digest to a14c5a6 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/97 +* chore(deps): update golang:1.24.0 docker digest to 58cf31c by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/98 +* chore(deps): update golang:1.24.0 docker digest to cd0c949 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/99 +* chore(deps): update golang:1.24.0 docker digest to 44b186e by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/101 +* chore(deps): update googleapis/release-please-action action to v4.1.4 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/102 +* chore(deps): update dependency developerc286/clean_git_history to v1 by @DeveloperC286 in https://github.com/DeveloperC286/unreferenced_files/pull/103 +* chore(deps): update golang:1.24.0 docker digest to 3f74443 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/104 +* chore(deps): update rust crate insta to v1.42.2 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/105 +* fix(deps): update rust crate anyhow to v1.0.97 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/106 +* chore(deps): update rust:1.85.0-alpine3.20 docker digest to f0cef6c by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/107 +* chore(deps): update golang docker tag to v1.24.1 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/108 +* chore(deps): update googleapis/release-please-action action to v4.1.5 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/109 +* chore(deps): update rust:1.85.0-alpine3.20 docker digest to c2f212d by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/110 +* chore(deps): update dependency mvdan/sh to v3.11.0 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/111 +* chore(deps): update googleapis/release-please-action action to v4.2.0 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/112 +* fix(deps): update rust crate clap to v4.5.32 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/113 +* chore(deps): update golang:1.24.1 docker digest to 8678013 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/114 +* chore(deps): update golang:1.24.1 docker digest to fa145a3 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/115 +* chore(deps): update golang:1.24.1 docker digest to ceb568d by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/116 +* chore(deps): update golang:1.24.1 docker digest to 762bb9c by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/117 +* chore(deps): update golang:1.24.1 docker digest to af0bb30 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/118 +* chore(deps): update rust docker tag to v1.85.1 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/119 +* chore(deps): update golang:1.24.1 docker digest to 52ff1b3 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/120 +* chore(deps): update rust:1.85.1-alpine3.20 docker digest to 4ec3fed by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/121 +* fix(deps): update rust crate log to v0.4.27 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/122 +* fix(deps): update rust crate clap to v4.5.34 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/123 +* chore(deps): update golang docker tag to v1.24.2 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/124 +* fix(deps): update rust crate clap to v4.5.35 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/125 +* chore(deps): update rust docker tag to v1.86.0 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/126 +* chore(deps): update rust:1.86.0-alpine3.20 docker digest to 2ee3527 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/127 +* chore(deps): update golang:1.24.2 docker digest to 37b19a8 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/128 +* chore(deps): update golang:1.24.2 docker digest to b51b7be by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/129 +* chore(deps): update golang:1.24.2 docker digest to fb224f9 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/130 +* chore(deps): update golang:1.24.2 docker digest to c0b66cf by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/131 +* chore(deps): update golang:1.24.2 docker digest to 227d106 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/132 +* chore(deps): update ubuntu:24.04 docker digest to 4524361 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/133 +* chore(deps): update dependency alpine_3_20/python3 to v3.12.10-r0 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/136 +* chore(deps): update golang:1.24.2 docker digest to 1ecc479 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/134 +* chore(deps): update ubuntu:24.04 docker digest to 1e622c5 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/135 +* chore(deps): update golang:1.24.2 docker digest to 18a1f2d by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/137 +* fix(deps): update rust crate clap to v4.5.36 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/139 +* fix(deps): update rust crate anyhow to v1.0.98 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/140 +* chore(deps): update golang:1.24.2 docker digest to 1ecc479 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/138 +* ci: Renovate update Rust's Alpine version regex by @DeveloperC286 in https://github.com/DeveloperC286/unreferenced_files/pull/141 +* ci: update inlined Alpine package versions by @DeveloperC286 in https://github.com/DeveloperC286/unreferenced_files/pull/143 +* ci: fixing check-clean-git-history.sh argument passing by @DeveloperC286 in https://github.com/DeveloperC286/unreferenced_files/pull/144 +* chore(deps): update golang:1.24.2 docker digest to d9db321 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/145 +* fix(deps): update rust crate clap to v4.5.37 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/146 +* chore(deps): update rust docker tag to v1.86.0-alpine3.21 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/142 +* chore(deps): update dependency alpine_3_21/github-cli to v2.63.0-r4 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/147 +* chore(deps): update rust crate insta to v1.43.0 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/148 +* chore(deps): update dependency developerc286/clean_git_history to v1.0.1 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/149 +* chore(deps): update golang:1.24.2 docker digest to 8131d99 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/150 +* chore(deps): update golang:1.24.2 docker digest to f52b85c by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/151 +* chore(deps): update golang:1.24.2 docker digest to 3a060d6 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/152 +* chore(deps): update rust crate insta to v1.43.1 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/153 +* chore(deps): update golang:1.24.2 docker digest to 30baaea by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/154 +* chore(deps): update rust:1.86.0-alpine3.21 docker digest to 8c042ca by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/155 +* chore(deps): update ubuntu:24.04 docker digest to 6015f66 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/156 +* chore(deps): update rust:1.86.0-alpine3.21 docker digest to 661d708 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/157 +* chore(deps): update golang docker tag to v1.24.3 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/158 +* fix(deps): update rust crate clap to v4.5.38 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/160 +* chore(deps): update dependency alpine_3_21/github-cli to v2.63.0-r5 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/161 +* chore(deps): update golang:1.24.3 docker digest to 86b4cff by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/162 +* chore(deps): update rust docker tag to v1.87.0 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/163 +* chore(deps): update dependency developerc286/clean_git_history to v1.0.2 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/164 +* chore(deps): update dependency alpine_3_21/python3 to v3.12.10-r1 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/165 +* chore(deps): update golang:1.24.3 docker digest to 1bcf884 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/166 +* chore(deps): update golang:1.24.3 docker digest to 02a2275 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/167 +* chore(deps): update golang:1.24.3 docker digest by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/168 +* chore(deps): update golang:1.24.3 docker digest to baf611f by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/169 +* chore(deps): update golang:1.24.3 docker digest to 795a40c by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/170 +* chore(deps): update golang:1.24.3 docker digest to 4c0a181 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/171 +* chore(deps): update dependency google/yamlfmt to v0.17.0 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/172 +* fix(deps): update rust crate clap to v4.5.39 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/173 +* chore(deps): update golang:1.24.3 docker digest to 81bf592 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/174 +* chore(deps): update ubuntu:24.04 docker digest to b59d215 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/176 +* chore(deps): update golang docker tag to v1.24.4 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/177 +* chore(deps): update dependency alpine_3_21/python3 to v3.12.11-r0 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/178 +* fix(deps): update rust crate clap to v4.5.40 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/179 +* chore(deps): update dependency developerc286/clean_git_history to v1.0.3 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/180 +* chore(config): migrate renovate config by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/159 +* build: replacing Earthly with Makefile & Docker build/run by @DeveloperC286 in https://github.com/DeveloperC286/unreferenced_files/pull/181 +* chore(deps): update python docker tag to v3.13.5 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/183 +* chore(deps): update python:3.13.5-alpine3.21 docker digest to c9a09c4 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/184 +* build: fixing publishing binary name by @DeveloperC286 in https://github.com/DeveloperC286/unreferenced_files/pull/185 +* ci: Renovate updating Makefile's Docker Images by @DeveloperC286 in https://github.com/DeveloperC286/unreferenced_files/pull/186 +* ci: simplifying mirroring workflow by @DeveloperC286 in https://github.com/DeveloperC286/unreferenced_files/pull/187 +* chore(deps): update ghcr.io/google/yamlfmt docker tag to v0.17.1 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/188 +* chore(deps): update ghcr.io/google/yamlfmt docker tag to v0.17.2 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/189 +* chore(deps): update dependency developerc286/clean_git_history to v1.0.4 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/190 +* ci: migrating to clean_git_history Docker image by @DeveloperC286 in https://github.com/DeveloperC286/unreferenced_files/pull/191 +* chore(deps): update rust docker tag to v1.88.0 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/192 +* ci: conventional commits linter Docker image by @DeveloperC286 in https://github.com/DeveloperC286/unreferenced_files/pull/195 +* refactor: GitHub Actions setting permissions by @DeveloperC286 in https://github.com/DeveloperC286/unreferenced_files/pull/196 +* ci: publishing Docker image by @DeveloperC286 in https://github.com/DeveloperC286/unreferenced_files/pull/197 + + +**Full Changelog**: https://github.com/DeveloperC286/unreferenced_files/compare/v2.1.0...v2.1.1 + +## 2.1.0 (2024-10-19) + +## What's Changed +* ci: removing GitLab CI/CD configuration by @DeveloperC286 in https://github.com/DeveloperC286/unreferenced_files/pull/3 +* docs: updating issues URL by @DeveloperC286 in https://github.com/DeveloperC286/unreferenced_files/pull/6 +* ci: adding GitLab mirroring GitHub Action by @DeveloperC286 in https://github.com/DeveloperC286/unreferenced_files/pull/7 +* ci: adding GitHub Actions Workflows linting by @DeveloperC286 in https://github.com/DeveloperC286/unreferenced_files/pull/8 +* ci: adding clean Git history workflow by @DeveloperC286 in https://github.com/DeveloperC286/unreferenced_files/pull/13 +* ci: adding Conventional Commits linting workflow by @DeveloperC286 in https://github.com/DeveloperC286/unreferenced_files/pull/14 +* ci: adding Continuous integration (CI) workflow by @DeveloperC286 in https://github.com/DeveloperC286/unreferenced_files/pull/15 +* docs: adding Continuous Integration(CI) badge by @DeveloperC286 in https://github.com/DeveloperC286/unreferenced_files/pull/16 +* docs: replacing GitLab Continuous Integration(CI) badge by @DeveloperC286 in https://github.com/DeveloperC286/unreferenced_files/pull/17 +* ci: moving GitHub Actions workflow formatting check by @DeveloperC286 in https://github.com/DeveloperC286/unreferenced_files/pull/18 +* docs: updating repository link to GitHub by @DeveloperC286 in https://github.com/DeveloperC286/unreferenced_files/pull/19 +* chore: Configure Renovate by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/21 +* chore(deps): update actions/checkout action to v4 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/25 +* docs: changing URLs to GitHub by @DeveloperC286 in https://github.com/DeveloperC286/unreferenced_files/pull/20 +* ci: converting to earthly/actions-setup@v1 by @DeveloperC286 in https://github.com/DeveloperC286/unreferenced_files/pull/31 +* docs: removing Continuous Integration (CI) badge by @DeveloperC286 in https://github.com/DeveloperC286/unreferenced_files/pull/32 +* docs: library deprecation notice by @DeveloperC286 in https://github.com/DeveloperC286/unreferenced_files/pull/34 +* refactor: merging binary and library by @DeveloperC286 in https://github.com/DeveloperC286/unreferenced_files/pull/33 +* feat: converting error handing to anyhow by @DeveloperC286 in https://github.com/DeveloperC286/unreferenced_files/pull/35 +* chore(deps): update dependency parse to v1.20.2 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/26 +* chore(deps): update dependency parse-type to v0.6.4 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/22 +* fix(deps): update rust crate anyhow to v1.0.90 by @renovate in https://github.com/DeveloperC286/unreferenced_files/pull/36 +* ci: adding release-please by @DeveloperC286 in https://github.com/DeveloperC286/unreferenced_files/pull/37 +* ci: adding release binary publishing by @DeveloperC286 in https://github.com/DeveloperC286/unreferenced_files/pull/39 +* ci: adding publishing to crates.io by @DeveloperC286 in https://github.com/DeveloperC286/unreferenced_files/pull/40 + +## New Contributors +* @DeveloperC286 made their first contribution in https://github.com/DeveloperC286/unreferenced_files/pull/3 +* @renovate made their first contribution in https://github.com/DeveloperC286/unreferenced_files/pull/21 + +**Full Changelog**: https://github.com/DeveloperC286/unreferenced_files/compare/2.0.0...v2.1.0 diff --git a/Cargo.lock b/Cargo.lock index da38eb4..5b739a6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,243 +1,396 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +version = 4 + [[package]] name = "aho-corasick" -version = "0.7.15" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" dependencies = [ "memchr", ] [[package]] -name = "ansi_term" -version = "0.11.0" +name = "anstream" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" +checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d" dependencies = [ - "winapi", + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", ] [[package]] -name = "atty" -version = "0.2.14" +name = "anstyle" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" + +[[package]] +name = "anstyle-parse" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e" dependencies = [ - "hermit-abi", - "libc", - "winapi", + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys", ] +[[package]] +name = "anyhow" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" + [[package]] name = "bitflags" -version = "1.2.1" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" [[package]] name = "cfg-if" -version = "1.0.0" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "clap" -version = "2.33.3" +version = "4.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" +checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51" dependencies = [ - "ansi_term", - "atty", - "bitflags", + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", "strsim", - "textwrap", - "unicode-width", - "vec_map", ] [[package]] -name = "console" -version = "0.14.1" +name = "clap_derive" +version = "4.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3993e6445baa160675931ec041a5e03ca84b9c6e32a056150d3aa2bdda0a1f45" +checksum = "f2ce8604710f6733aa641a2b3731eaa1e8b3d9973d5e3565da11800813f997a9" dependencies = [ - "encode_unicode", - "lazy_static", - "libc", - "terminal_size", - "winapi", + "heck", + "proc-macro2", + "quote", + "syn", ] [[package]] -name = "dtoa" -version = "0.4.8" +name = "clap_lex" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56899898ce76aaf4a0f24d914c97ea6ed976d42fec6ad33fcbb0a1103e07b2b0" +checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" + +[[package]] +name = "colorchoice" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" + +[[package]] +name = "console" +version = "0.16.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d64e8af5551369d19cf50138de61f1c42074ab970f74e99be916646777f8fc87" +dependencies = [ + "encode_unicode", + "libc", + "windows-sys", +] [[package]] name = "encode_unicode" -version = "0.3.6" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" +checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" [[package]] name = "env_logger" -version = "0.7.1" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" +checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580" dependencies = [ - "atty", "humantime", + "is-terminal", "log", "regex", "termcolor", ] [[package]] -name = "heck" -version = "0.3.2" +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cbf45460356b7deeb5e3415b5563308c0a9b057c85e12b06ad551f98d0a6ac" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ - "unicode-segmentation", + "libc", + "windows-sys", ] [[package]] -name = "hermit-abi" -version = "0.1.18" +name = "fastrand" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c" +checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "getrandom" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" dependencies = [ + "cfg-if", "libc", + "r-efi", + "wasip2", + "wasip3", ] +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash", +] + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" + +[[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.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" + [[package]] name = "humantime" -version = "1.3.0" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "135b12329e5e3ce057a9f972339ea52bc954fe1e9358ef27f95e89716fbc5424" + +[[package]] +name = "id-arena" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + +[[package]] +name = "indexmap" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" dependencies = [ - "quick-error", + "equivalent", + "hashbrown 0.17.1", + "serde", + "serde_core", ] [[package]] name = "insta" -version = "1.7.1" +version = "1.47.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4a1b21a2971cea49ca4613c0e9fe8225ecaf5de64090fddc6002284726e9244" +checksum = "7b4a6248eb93a4401ed2f37dfe8ea592d3cf05b7cf4f8efa867b6895af7e094e" dependencies = [ "console", - "lazy_static", - "serde", - "serde_json", - "serde_yaml", + "once_cell", "similar", - "uuid", + "tempfile", ] +[[package]] +name = "is-terminal" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" +dependencies = [ + "hermit-abi", + "libc", + "windows-sys", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" + [[package]] name = "itoa" -version = "0.4.7" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "lazy_static" -version = "1.4.0" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "leb128fmt" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" [[package]] name = "libc" -version = "0.2.94" +version = "0.2.186" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18794a8ad5b29321f790b55d93dfba91e125cb1a9edbd4f8e3150acc771c1a5e" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" [[package]] -name = "linked-hash-map" -version = "0.5.4" +name = "linux-raw-sys" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" [[package]] name = "log" -version = "0.4.14" +version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" -dependencies = [ - "cfg-if", -] +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" [[package]] name = "memchr" -version = "2.3.4" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + +[[package]] +name = "once_cell" +version = "1.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "once_cell_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" [[package]] name = "pretty_env_logger" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "926d36b9553851b8b0005f1275891b392ee4d2d833852c417ed025477350fb9d" +checksum = "865724d4dbe39d9f3dd3b52b88d859d66bcb2d6a0acfd5ea68a65fb66d4bdc1c" dependencies = [ "env_logger", "log", ] [[package]] -name = "proc-macro-error" -version = "1.0.4" +name = "prettyplease" +version = "0.2.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ - "proc-macro-error-attr", "proc-macro2", - "quote", "syn", - "version_check", ] [[package]] -name = "proc-macro-error-attr" -version = "1.0.4" +name = "proc-macro2" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" dependencies = [ - "proc-macro2", - "quote", - "version_check", + "unicode-ident", ] [[package]] -name = "proc-macro2" -version = "1.0.26" +name = "quote" +version = "1.0.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a152013215dca273577e18d2bf00fa862b89b24169fb78c4c95aeb07992c9cec" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" dependencies = [ - "unicode-xid", + "proc-macro2", ] [[package]] -name = "quick-error" -version = "1.2.3" +name = "r-efi" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" [[package]] -name = "quote" -version = "1.0.9" +name = "regex" +version = "1.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" dependencies = [ - "proc-macro2", + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", ] [[package]] -name = "regex" -version = "1.4.6" +name = "regex-automata" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a26af418b574bd56588335b3a3659a65725d4e636eb1016c2f9e3b38c7cc759" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" dependencies = [ "aho-corasick", "memchr", @@ -246,30 +399,52 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.23" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5f089152e60f62d28b835fbff2cd2e8dc0baf1ac13343bef92ab7eed84548" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" [[package]] -name = "ryu" -version = "1.0.5" +name = "rustix" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" [[package]] name = "serde" -version = "1.0.125" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "558dc50e1a5a5fa7112ca2ce4effcb321b0300c0d4ccf0776a9f60cd89031171" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.125" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b093b7a2bb58203b5da3056c05b4ec1fed827dcfdb37347a8841695263b3d06d" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", @@ -278,186 +453,265 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.64" +version = "1.0.149" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "799e97dc9fdae36a5c8b8f2cae9ce2ee9fdce2058c57a93e6099d919fd982f79" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" dependencies = [ "itoa", - "ryu", + "memchr", "serde", + "serde_core", + "zmij", ] [[package]] -name = "serde_yaml" -version = "0.8.17" +name = "similar" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15654ed4ab61726bf918a39cb8d98a2e2995b002387807fa6ba58fdf7f59bb23" +checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" dependencies = [ - "dtoa", - "linked-hash-map", - "serde", - "yaml-rust", + "proc-macro2", + "quote", + "unicode-ident", ] [[package]] -name = "similar" -version = "1.3.0" +name = "tempfile" +version = "3.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ad1d488a557b235fc46dae55512ffbfc429d2482b08b4d9435ab07384ca8aec" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" +dependencies = [ + "fastrand", + "getrandom", + "once_cell", + "rustix", + "windows-sys", +] [[package]] -name = "strsim" -version = "0.8.0" +name = "termcolor" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] [[package]] -name = "structopt" -version = "0.3.21" +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-xid" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5277acd7ee46e63e5168a80734c9f6ee81b1367a7d8772a2d765df2a3705d28c" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "unreferenced_files" +version = "2.2.0" dependencies = [ + "anyhow", "clap", + "insta", "lazy_static", - "structopt-derive", + "log", + "pretty_env_logger", + "regex", ] [[package]] -name = "structopt-derive" -version = "0.4.14" +name = "utf8parse" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ba9cdfda491b814720b6b06e0cac513d922fc407582032e8706e9f137976f90" -dependencies = [ - "heck", - "proc-macro-error", - "proc-macro2", - "quote", - "syn", -] +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] -name = "syn" -version = "1.0.71" +name = "wasip2" +version = "1.0.3+wasi-0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad184cc9470f9117b2ac6817bfe297307418819ba40552f9b3846f05c33d5373" +checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6" dependencies = [ - "proc-macro2", - "quote", - "unicode-xid", + "wit-bindgen 0.57.1", ] [[package]] -name = "termcolor" -version = "1.1.2" +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" dependencies = [ - "winapi-util", + "wit-bindgen 0.51.0", ] [[package]] -name = "terminal_size" -version = "0.1.16" +name = "wasm-encoder" +version = "0.244.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86ca8ced750734db02076f44132d802af0b33b09942331f4459dde8636fd2406" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" dependencies = [ - "libc", - "winapi", + "leb128fmt", + "wasmparser", ] [[package]] -name = "textwrap" -version = "0.11.0" +name = "wasm-metadata" +version = "0.244.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" dependencies = [ - "unicode-width", + "anyhow", + "indexmap", + "wasm-encoder", + "wasmparser", ] [[package]] -name = "unicode-segmentation" -version = "1.7.1" +name = "wasmparser" +version = "0.244.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb0d2e7be6ae3a5fa87eed5fb451aff96f2573d2694942e40543ae0bbe19c796" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags", + "hashbrown 0.15.5", + "indexmap", + "semver", +] [[package]] -name = "unicode-width" -version = "0.1.8" +name = "winapi-util" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys", +] [[package]] -name = "unicode-xid" -version = "0.2.2" +name = "windows-link" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" [[package]] -name = "unreferenced_files" -version = "2.0.0" +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" dependencies = [ - "insta", - "lazy_static", - "log", - "pretty_env_logger", - "regex", - "structopt", + "windows-link", ] [[package]] -name = "uuid" -version = "0.8.2" +name = "wit-bindgen" +version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +dependencies = [ + "wit-bindgen-rust-macro", +] [[package]] -name = "vec_map" -version = "0.8.2" +name = "wit-bindgen" +version = "0.57.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" [[package]] -name = "version_check" -version = "0.9.3" +name = "wit-bindgen-core" +version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck", + "wit-parser", +] [[package]] -name = "winapi" -version = "0.3.9" +name = "wit-bindgen-rust" +version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", + "anyhow", + "heck", + "indexmap", + "prettyplease", + "syn", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", ] [[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" +name = "wit-bindgen-rust-macro" +version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn", + "wit-bindgen-core", + "wit-bindgen-rust", +] [[package]] -name = "winapi-util" -version = "0.1.5" +name = "wit-component" +version = "0.244.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" dependencies = [ - "winapi", + "anyhow", + "bitflags", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", ] [[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" +name = "wit-parser" +version = "0.244.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] [[package]] -name = "yaml-rust" -version = "0.4.5" +name = "zmij" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" -dependencies = [ - "linked-hash-map", -] +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/Cargo.toml b/Cargo.toml index cb736d9..a5034b8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,28 +1,32 @@ [package] name = "unreferenced_files" description = "A utility for finding unused and unreferenced files." -version = "2.0.0" +version = "2.2.0" authors = ["C "] -edition = "2018" +edition = "2021" license = "AGPL-3.0" -repository = "https://gitlab.com/DeveloperC/unreferenced_files" +repository = "https://github.com/DeveloperC286/unreferenced_files" readme = "README.md" keywords = ["find", "unreferenced", "files", "unused"] -categories = ["command-line-utilities", "development-tools"] +categories = ["command-line-utilities", "development-tools", "parser-implementations", "text-processing", "data-structures"] [dependencies] -structopt = "0.3.21" -regex = "1.4.6" -log = "0.4.14" -pretty_env_logger = "0.4.0" +# For CLI parsing. +clap = { version = "4.4.6", features = ["derive"] } +# For logging. +log = "0.4.20" +pretty_env_logger = "0.5.0" -[dev-dependencies] -insta = "1.7.1" -lazy_static = "1.4.0" +# For searching for references in files. +regex = "1.9.6" + +# For error handling. +anyhow = "1.0.89" -[profile.release] -lto = 'fat' -codegen-units = 1 +[dev-dependencies] +# For snapshot testing. +insta = "=1.47.2" +lazy_static = "=1.5.0" diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..3ad9ea9 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,8 @@ +FROM alpine:3.23.4@sha256:5b10f432ef3da1b8d4c7eb6c487f2f5a8f096bc91145e68878dd4a5019afde11 + +ARG TARGET +COPY ./target/${TARGET}/release/unreferenced_files /usr/local/bin/ + +WORKDIR /workspace + +ENTRYPOINT ["unreferenced_files"] diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..1ac0839 --- /dev/null +++ b/Makefile @@ -0,0 +1,114 @@ +# Auto-detect musl target for static binaries (Linux only) +# Only set MUSL_TARGET on supported architectures; targets that need it will check +MUSL_TARGET := $(shell uname -m | sed 's/^x86_64$$/x86_64-unknown-linux-musl/;s/^aarch64$$/aarch64-unknown-linux-musl/') + +define check-musl-target +$(if $(filter %unknown-linux-musl,$(MUSL_TARGET)),,$(error Unsupported architecture: $(shell uname -m). Static musl builds only supported on Linux x86_64 and aarch64)) +endef + +# Use --locked in CI to ensure reproducible builds +CARGO_LOCKED := $(if $(CI),--locked,) + +.PHONY: default +default: compile + +.PHONY: check-shell-permissions +check-shell-permissions: + ./ci/check-scripts-permissions.sh + +.PHONY: check-rust-formatting +check-rust-formatting: + cargo fmt --all -- --check --config=group_imports=StdExternalCrate + +.PHONY: fix-rust-formatting +fix-rust-formatting: + cargo fmt --all -- --config=group_imports=StdExternalCrate + +.PHONY: check-shell-formatting +check-shell-formatting: + shfmt --simplify --diff ci/* + +.PHONY: fix-shell-formatting +fix-shell-formatting: + shfmt --simplify --write ci/* + +.PHONY: check-python-formatting +check-python-formatting: + autopep8 --exit-code --diff --aggressive --aggressive --max-line-length 120 --recursive end-to-end-tests/ + +.PHONY: fix-python-formatting +fix-python-formatting: + autopep8 --in-place --aggressive --aggressive --max-line-length 120 --recursive end-to-end-tests/ + +.PHONY: check-yaml-formatting +check-yaml-formatting: + yamlfmt -verbose -lint -dstar .github/workflows/* + +.PHONY: fix-yaml-formatting +fix-yaml-formatting: + yamlfmt -verbose -dstar .github/workflows/* + +.PHONY: check-rust-linting +check-rust-linting: + cargo clippy --verbose $(CARGO_LOCKED) -- -D warnings + +.PHONY: check-shell-linting +check-shell-linting: + shellcheck ci/*.sh + +.PHONY: check-python-linting +check-python-linting: + ruff check --line-length 120 end-to-end-tests/ + +.PHONY: fix-python-linting +fix-python-linting: + ruff check --fix --line-length 120 end-to-end-tests/ + +.PHONY: check-github-actions-workflows-linting +check-github-actions-workflows-linting: + actionlint -verbose -color + +.PHONY: check-rust-dependencies +check-rust-dependencies: + cargo machete + +.PHONY: compile +compile: + cargo build --verbose $(CARGO_LOCKED) + +.PHONY: unit-test +unit-test: + cargo test --verbose $(CARGO_LOCKED) + +.PHONY: end-to-end-test +end-to-end-test: compile + cd end-to-end-tests/ && behave + +.PHONY: release +release: + $(call check-musl-target) + cargo build --release --target=$(MUSL_TARGET) --locked --verbose + +.PHONY: publish-binary +publish-binary: release + ./ci/publish-binary.sh ${RELEASE} $(MUSL_TARGET) + +.PHONY: publish-crate +publish-crate: + cargo publish --verbose + +# Emulate GitHub Actions CI environment for testing +GITHUB_ACTIONS_ENV := --env HOME=/github/home --env GITHUB_ACTIONS=true --env CI=true + +.PHONY: dogfood-docker +dogfood-docker: release + docker build --build-arg TARGET=$(MUSL_TARGET) --tag unreferenced_files --file Dockerfile . + docker run --rm --volume $(PWD):/workspace --workdir /workspace $(GITHUB_ACTIONS_ENV) unreferenced_files --help + +.PHONY: publish-docker-image +publish-docker-image: + ./ci/publish-docker-image.sh ${RELEASE} ${PLATFORM} ${TARGET} ${SUFFIX} + +.PHONY: publish-docker-manifest +publish-docker-manifest: + ./ci/publish-docker-manifest.sh ${RELEASE} diff --git a/README.md b/README.md index 04ec604..5944045 100644 --- a/README.md +++ b/README.md @@ -1,19 +1,20 @@ # Unreferenced Files -[![crates.io](https://img.shields.io/crates/v/unreferenced_files)](https://crates.io/crates/unreferenced_files) [![pipeline status](https://gitlab.com/DeveloperC/unreferenced_files/badges/master/pipeline.svg)](https://gitlab.com/DeveloperC/unreferenced_files/commits/master) [![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-yellow.svg)](https://conventionalcommits.org) [![License: AGPL v3](https://img.shields.io/badge/License-AGPLv3-blue.svg)](https://www.gnu.org/licenses/agpl-3.0) - +[![crates.io](https://img.shields.io/crates/v/unreferenced_files)](https://crates.io/crates/unreferenced_files) +[![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-yellow.svg)](https://conventionalcommits.org) +[![License](https://img.shields.io/badge/License-AGPLv3-blue.svg)](https://www.gnu.org/licenses/agpl-3.0) A utility for finding unused and unreferenced files. - -## Content - * [Usage](#usage) - + [Usage - Additional Arguments](#usage-additional-arguments) - + [Usage - Example](#usage-example) - + [Usage - Logging](#usage-logging) - * [Compiling via Local Repository](#compiling-via-local-repository) - * [Compiling via Cargo](#compiling-via-cargo) - * [Issues/Feature Requests](#issuesfeature-requests) - +- [Usage](#usage) + - [Additional Arguments](#additional-arguments) + - [Example](#example) +- [Examples](#examples) + - [GitHub Actions](#github-actions) + - [GitLab CI](#gitlab-ci) +- [Installation](#installation) + - [Binary](#binary) + - [Cargo](#cargo) + - [Docker](#docker) ## Usage `unreferenced_files` is a very simple and fast tool. @@ -25,20 +26,19 @@ If the search for and search files overlap then a file will not search itself to e.g. -``` -> tree parent +```sh +tree parent parent/ ├── child -│   └── file2.txt +│ └── file2.txt └── file1.txt ``` For the example directory above, if the argument was `--search-for parent/` then for the file `parent/file1.txt` the relative path of `parent/file1.txt`, the file name `file1.txt`, and the file stem `file1` would be searched for. For the file `parent/child/file2.txt` the relative path of `parent/child/file2.txt`, the file name `file2.txt` and file stem `file2` would be searched for. - -## Usage - Additional Arguments +### Additional Arguments Additional command line flags can be passed to alter what is searched for to determine if a file is referenced. @@ -54,11 +54,10 @@ Additional command line flags can be passed to alter what is searched for to det | --print-full-path | Output the full path of each unreferenced file, instead of the relative path. | | --assert-no-unreferenced-files | Return a nonzero exit code if there are any unreferenced files. | +### Example +For an example Java project with tests referencing files inside `src/test/resources/` where the tests are calling the files by name e.g. -### Usage - Example -For an example Java project with tests referencing files inside `src/test/resources/` where the tests are calling the files by name e.g. - -``` +```java @Test public void testImportingFile() { ... @@ -71,39 +70,91 @@ public void testImportingFile() { You can find all the unreferenced files inside `src/test/resources/` via -``` +```sh cd src/test/resources/ unreferenced_files --search-for ./ --search ../java/ ``` +## Examples +### GitHub Actions + +```yaml +name: CI + +on: pull_request + +jobs: + check-unreferenced-files: + name: Check Unreferenced Files + runs-on: ubuntu-latest + steps: + - name: Checkout code. + uses: actions/checkout@v4 + - name: Install Unreferenced Files. + run: version="v2.2.0" && wget -O - "https://github.com/DeveloperC286/unreferenced_files/releases/download/${version}/x86_64-unknown-linux-musl.tar.gz" | tar xz --directory "/usr/bin/" + - name: Check for unreferenced files + run: unreferenced_files --search-for src/test/resources/ --search src/test/java/ --assert-no-unreferenced-files +``` + + +### GitLab CI + +```yaml +check-unreferenced-files: + stage: check-unreferenced-files + image: rust + before_script: + - version="v2.2.0" && wget -O - "https://github.com/DeveloperC286/unreferenced_files/releases/download/${version}/x86_64-unknown-linux-musl.tar.gz" | tar xz --directory "/usr/bin/" + script: + - unreferenced_files --search-for src/test/resources/ --search src/test/java/ --assert-no-unreferenced-files + rules: + - if: $CI_MERGE_REQUEST_ID -### Usage - Logging -The crates `pretty_env_logger` and `log` are used to provide logging. -The environment variable `RUST_LOG` can be used to set the logging level. -See [https://crates.io/crates/pretty_env_logger](https://crates.io/crates/pretty_env_logger) for more detailed documentation. - +``` + -## Compiling via Local Repository -Checkout the code repository locally, change into the repository's directory and then build via cargo. -Using the `--release` flag produces an optimised binary but takes longer to compile. +## Installation +### Binary + +```sh +version="v2.2.0" && wget -O - "https://github.com/DeveloperC286/unreferenced_files/releases/download/${version}/x86_64-unknown-linux-musl.tar.gz" | tar xz --directory "/usr/bin/" ``` -git clone git@gitlab.com:DeveloperC/unreferenced_files.git -cd unreferenced_files/ -cargo build --release -``` + + +### Cargo +Cargo is the Rust package manager, the `install` sub-command pulls from [crates.io](https://crates.io/crates/unreferenced_files) and then compiles the binary locally, placing the compiled binary at `${HOME}/.cargo/bin/unreferenced_files`. -The compiled binary is present in `target/release/unreferenced_files`. +```sh +cargo install unreferenced_files +``` +By default it installs the latest version at the time of execution. +You can specify a specific version to install using the `--version` argument. +For certain environments such as CICD etc you may want to pin the version. -## Compiling via Cargo -Cargo is the Rust package manager, using the `install` sub-command it pulls the crate from `crates.io` and then compiles the binary locally. -`cargo install` places the produced binary at `$HOME/.cargo/bin/unreferenced_files`. + +e.g. +```sh +cargo install unreferenced_files --version 2.2.0 ``` -cargo install unreferenced_files + + +Rather than pinning to a specific version you can specify the major or minor version. + + +e.g. + +```sh +cargo install unreferenced_files --version ^2 ``` + + +Will download the latest `2.*` release whether that is `2.0.0` or `2.7.0`. +### Docker +You can use the Docker image published to [ghcr.io/developerc286/unreferenced_files](https://github.com/DeveloperC286/unreferenced_files/pkgs/container/unreferenced_files). ## Issues/Feature Requests -To report an issue or request a new feature use [https://gitlab.com/DeveloperC/unreferenced_files/-/issues](https://gitlab.com/DeveloperC/unreferenced_files/-/issues). +Report issues or request features on our [GitHub Issues](https://github.com/DeveloperC286/unreferenced_files/issues). diff --git a/ci/check-scripts-permissions.sh b/ci/check-scripts-permissions.sh new file mode 100755 index 0000000..67821de --- /dev/null +++ b/ci/check-scripts-permissions.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env sh + +set -o errexit +set -o xtrace + +exit_code=0 +for script in ci/*.sh; do + if [ -f "$script" ] && [ ! -x "$script" ]; then + exit_code=1 + fi +done + +exit $exit_code diff --git a/ci/publish-binary.sh b/ci/publish-binary.sh new file mode 100755 index 0000000..9cc851c --- /dev/null +++ b/ci/publish-binary.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env sh + +set -o errexit +set -o xtrace + +if [ "$#" -ne 2 ]; then + echo "Usage: $0 RELEASE_TAG TARGET" + echo "$#" + exit 1 +fi + +RELEASE="$1" +TARGET="$2" + +tar -czvf "${TARGET}.tar.gz" -C "target/${TARGET}/release" "unreferenced_files" +gh release upload "${RELEASE}" "${TARGET}.tar.gz" +rm "${TARGET}.tar.gz" diff --git a/ci/publish-docker-image.sh b/ci/publish-docker-image.sh new file mode 100755 index 0000000..a078c21 --- /dev/null +++ b/ci/publish-docker-image.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env sh + +set -o errexit +set -o xtrace + +if [ $# -ne 4 ]; then + echo "Usage: $0 " + exit 1 +fi + +RELEASE="$1" +PLATFORM="$2" +TARGET="$3" +SUFFIX="$4" + +REPOSITORY="$(basename "$(git rev-parse --show-toplevel)")" +IMAGE="ghcr.io/developerc286/${REPOSITORY}" + +# Download and extract pre-built binary from the GitHub release for this architecture. +gh release download "${RELEASE}" --pattern "${TARGET}.tar.gz" +mkdir -p "target/${TARGET}/release" +tar -xzf "${TARGET}.tar.gz" -C "target/${TARGET}/release" + +# Build and push the Docker image for this architecture natively (no QEMU emulation). +docker buildx build --platform "${PLATFORM}" --build-arg TARGET="${TARGET}" --tag "${IMAGE}:${RELEASE}-${SUFFIX}" --file Dockerfile . --push diff --git a/ci/publish-docker-manifest.sh b/ci/publish-docker-manifest.sh new file mode 100755 index 0000000..7c203c5 --- /dev/null +++ b/ci/publish-docker-manifest.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env sh + +set -o errexit +set -o xtrace + +if [ $# -ne 1 ]; then + echo "Usage: $0 " + exit 1 +fi + +RELEASE="$1" +REPOSITORY="$(basename "$(git rev-parse --show-toplevel)")" +IMAGE="ghcr.io/developerc286/${REPOSITORY}" + +# Create and push the multi-architecture manifest. +docker buildx imagetools create --tag "${IMAGE}:${RELEASE}" \ + "${IMAGE}:${RELEASE}-amd64" \ + "${IMAGE}:${RELEASE}-arm64" + +# Create alternate version tag (with/without 'v' prefix). +if [ "${RELEASE#v}" != "${RELEASE}" ]; then + # Release has 'v' prefix (v1.2.3), also create version without 'v' prefix (1.2.3). + docker buildx imagetools create --tag "${IMAGE}:${RELEASE#v}" "${IMAGE}:${RELEASE}" +else + # Release has no 'v' prefix (1.2.3), also create version with 'v' prefix (v1.2.3). + docker buildx imagetools create --tag "${IMAGE}:v${RELEASE}" "${IMAGE}:${RELEASE}" +fi diff --git a/end-to-end-tests/.gitignore b/end-to-end-tests/.gitignore new file mode 100644 index 0000000..3cbd32e --- /dev/null +++ b/end-to-end-tests/.gitignore @@ -0,0 +1,35 @@ +# Python .gitignore +# Generated by Python virtual environments. +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Generated by byte-compiled / optimized / DLL files. +__pycache__/ +*.py[cod] + +# Generated by Python distribution / packaging. +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# Generated by IntelliJ IDEA. +.idea/ diff --git a/end-to-end-tests/features/accepts-directories-as-input.feature b/end-to-end-tests/features/accepts_directories_as_input.feature similarity index 97% rename from end-to-end-tests/features/accepts-directories-as-input.feature rename to end-to-end-tests/features/accepts_directories_as_input.feature index aea1ae6..e959e5c 100644 --- a/end-to-end-tests/features/accepts-directories-as-input.feature +++ b/end-to-end-tests/features/accepts_directories_as_input.feature @@ -7,7 +7,6 @@ Feature: Unreferenced Files can handle directories being passed as parameters to And the argument --search-for is provided as "". And the flag --assert-no-unreferenced-files is set. Then the unreferenced files are "". - And the status code is nonzero. Examples: | repository | checkout_commit | search | search_for | unreferenced_files | diff --git a/end-to-end-tests/features/accepts-files-as-input.feature b/end-to-end-tests/features/accepts_files_as_input.feature similarity index 97% rename from end-to-end-tests/features/accepts-files-as-input.feature rename to end-to-end-tests/features/accepts_files_as_input.feature index f36675d..d2c22a8 100644 --- a/end-to-end-tests/features/accepts-files-as-input.feature +++ b/end-to-end-tests/features/accepts_files_as_input.feature @@ -7,7 +7,6 @@ Feature: Unreferenced Files can handle files being passed as parameters to searc And the argument --search-for is provided as "". And the flag --assert-no-unreferenced-files is set. Then the unreferenced files are "". - And the status code is nonzero. Examples: | repository | checkout_commit | search | search_for | unreferenced_files | diff --git a/end-to-end-tests/features/accepts-multiple-files-and-directories-as-input.feature b/end-to-end-tests/features/accepts_multiple_files_and_directories_as_input.feature similarity index 98% rename from end-to-end-tests/features/accepts-multiple-files-and-directories-as-input.feature rename to end-to-end-tests/features/accepts_multiple_files_and_directories_as_input.feature index 48f4ced..0fc3914 100644 --- a/end-to-end-tests/features/accepts-multiple-files-and-directories-as-input.feature +++ b/end-to-end-tests/features/accepts_multiple_files_and_directories_as_input.feature @@ -8,7 +8,6 @@ Feature: Unreferenced Files can handle multiple files and directories being pass And the argument --search-for is provided as "". And the flag --assert-no-unreferenced-files is set. Then the unreferenced files are "". - And the status code is nonzero. Examples: | repository | checkout_commit | search_file | search_for_file_1 | search_for_file_2 | unreferenced_files | diff --git a/end-to-end-tests/features/assert-no-unreferenced-files.feature b/end-to-end-tests/features/assert_no_unreferenced_files.feature similarity index 98% rename from end-to-end-tests/features/assert-no-unreferenced-files.feature rename to end-to-end-tests/features/assert_no_unreferenced_files.feature index 2273dfe..643f404 100644 --- a/end-to-end-tests/features/assert-no-unreferenced-files.feature +++ b/end-to-end-tests/features/assert_no_unreferenced_files.feature @@ -7,7 +7,6 @@ Feature: When using the --assert-no-unreferenced-files flag if unreferenced file And the argument --search-for is provided as "". And the flag --assert-no-unreferenced-files is set. Then the unreferenced files are "". - And the status code is nonzero. Examples: | repository | checkout_commit | search | search_for | unreferenced_files | diff --git a/end-to-end-tests/features/ignore-search.feature b/end-to-end-tests/features/ignore_search.feature similarity index 96% rename from end-to-end-tests/features/ignore-search.feature rename to end-to-end-tests/features/ignore_search.feature index 5007ef9..b80fdc3 100644 --- a/end-to-end-tests/features/ignore-search.feature +++ b/end-to-end-tests/features/ignore_search.feature @@ -9,7 +9,6 @@ Feature: Ignore and do not search any files that match any of the provided ignor Then unreferenced files are not found. When the argument --ignore-search is provided as "". Then the unreferenced files are "". - And the status code is nonzero. Examples: @@ -40,10 +39,8 @@ Feature: Ignore and do not search any files that match any of the provided ignor And the flag --assert-no-unreferenced-files is set. And the flag --only-file-name is set. Then the unreferenced files are "". - And the status code is nonzero. When the argument --ignore-search is provided as "". Then the unreferenced files are "". - And the status code is nonzero. Examples: diff --git a/end-to-end-tests/features/ignore-search-for.feature b/end-to-end-tests/features/ignore_search_for.feature similarity index 96% rename from end-to-end-tests/features/ignore-search-for.feature rename to end-to-end-tests/features/ignore_search_for.feature index 2ef418a..1f2ab54 100644 --- a/end-to-end-tests/features/ignore-search-for.feature +++ b/end-to-end-tests/features/ignore_search_for.feature @@ -8,7 +8,6 @@ Feature: Ignore and do not search for any files that match any of the provided i And the flag --assert-no-unreferenced-files is set. And the flag --only-file-name is set. Then the unreferenced files are "". - And the status code is nonzero. When the argument --ignore-search-for is provided as "". Then unreferenced files are not found. @@ -24,10 +23,8 @@ Feature: Ignore and do not search for any files that match any of the provided i And the argument --search-for is provided as "". And the flag --assert-no-unreferenced-files is set. Then the unreferenced files are "". - And the status code is nonzero. When the argument --ignore-search-for is provided as "". Then the unreferenced files are "". - And the status code is nonzero. Examples: diff --git a/end-to-end-tests/features/only-search.feature b/end-to-end-tests/features/only_search.feature similarity index 96% rename from end-to-end-tests/features/only-search.feature rename to end-to-end-tests/features/only_search.feature index 43d08fd..b6323a1 100644 --- a/end-to-end-tests/features/only-search.feature +++ b/end-to-end-tests/features/only_search.feature @@ -9,7 +9,6 @@ Feature: Only search files that match any of the provided only-search regexes. Then unreferenced files are not found. When the argument --only-search is provided as "". Then the unreferenced files are "". - And the status code is nonzero. Examples: @@ -40,10 +39,8 @@ Feature: Only search files that match any of the provided only-search regexes. And the flag --assert-no-unreferenced-files is set. And the flag --only-file-name is set. Then the unreferenced files are "". - And the status code is nonzero. When the argument --only-search is provided as "". Then the unreferenced files are "". - And the status code is nonzero. Examples: diff --git a/end-to-end-tests/features/only-search-for.feature b/end-to-end-tests/features/only_search_for.feature similarity index 96% rename from end-to-end-tests/features/only-search-for.feature rename to end-to-end-tests/features/only_search_for.feature index 14d42e3..5dc31ca 100644 --- a/end-to-end-tests/features/only-search-for.feature +++ b/end-to-end-tests/features/only_search_for.feature @@ -8,7 +8,6 @@ Feature: Only search for files that match any of the provided only-search-for re And the flag --assert-no-unreferenced-files is set. And the flag --only-file-name is set. Then the unreferenced files are "". - And the status code is nonzero. When the argument --only-search-for is provided as "". Then unreferenced files are not found. @@ -24,10 +23,8 @@ Feature: Only search for files that match any of the provided only-search-for re And the argument --search-for is provided as "". And the flag --assert-no-unreferenced-files is set. Then the unreferenced files are "". - And the status code is nonzero. When the argument --only-search-for is provided as "". Then the unreferenced files are "". - And the status code is nonzero. Examples: diff --git a/end-to-end-tests/features/requires-search-and-search-for.feature b/end-to-end-tests/features/requires_search_and_search_for.feature similarity index 93% rename from end-to-end-tests/features/requires-search-and-search-for.feature rename to end-to-end-tests/features/requires_search_and_search_for.feature index 50e6644..e44010a 100644 --- a/end-to-end-tests/features/requires-search-and-search-for.feature +++ b/end-to-end-tests/features/requires_search_and_search_for.feature @@ -5,7 +5,6 @@ Feature: Unreferenced Files requires the parameters --search and --search-for. Given the repository "" is cloned and checked out at the commit "". When the argument --search-for is provided as "". Then printed is an error message detailing that the argument search is missing. - And the status code is nonzero. Examples: | repository | checkout_commit | search_for | @@ -16,7 +15,6 @@ Feature: Unreferenced Files requires the parameters --search and --search-for. Given the repository "" is cloned and checked out at the commit "". When the argument --search is provided as "". Then printed is an error message detailing that the argument search for is missing. - And the status code is nonzero. Examples: | repository | checkout_commit | search | diff --git a/end-to-end-tests/features/search-filters-mutually-exclusive.feature b/end-to-end-tests/features/search_filters_mutually_exclusive.feature similarity index 100% rename from end-to-end-tests/features/search-filters-mutually-exclusive.feature rename to end-to-end-tests/features/search_filters_mutually_exclusive.feature diff --git a/end-to-end-tests/features/search-for-filters-mutually-exclusive.feature b/end-to-end-tests/features/search_for_filters_mutually_exclusive.feature similarity index 100% rename from end-to-end-tests/features/search-for-filters-mutually-exclusive.feature rename to end-to-end-tests/features/search_for_filters_mutually_exclusive.feature diff --git a/end-to-end-tests/features/steps/assertions.py b/end-to-end-tests/features/steps/assertions.py index 697e3b3..0375df0 100644 --- a/end-to-end-tests/features/steps/assertions.py +++ b/end-to-end-tests/features/steps/assertions.py @@ -1,80 +1,39 @@ -import os -from behave import * +def assert_command_successful(result): + assert result.exit_code == 0, "Expected a zero exit code to indicate a successful execution.\n" + \ + f"Exit code = '{result.exit_code}'.\n" -from util import execute_command -MISSING_SEARCH = "error: The following required arguments were not provided:\n --search ...\n\n" -MISSING_SEARCH_FOR = "error: The following required arguments were not provided:\n --search-for ...\n\n" -ONLY_SEARCH_MUTUALLY_EXCLUSIVE = "error: The argument '--only-search ...' cannot be used with one or more of the other specified arguments\n" -IGNORE_SEARCH_MUTUALLY_EXCLUSIVE = "error: The argument '--ignore-search ...' cannot be used with one or more of the other specified arguments\n" -ONLY_SEARCH_FOR_MUTUALLY_EXCLUSIVE = "error: The argument '--only-search-for ...' cannot be used with one or more of the other specified arguments\n" -IGNORE_SEARCH_FOR_MUTUALLY_EXCLUSIVE = "error: The argument '--ignore-search-for ...' cannot be used with one or more of the other specified arguments\n" +def assert_command_unsuccessful(result): + assert result.exit_code != 0, "Expected a non-zero exit code to indicate a unsuccessful execution\n" + \ + f"Exit code = '{result.exit_code}'.\n" -@then('unreferenced files are not found.') -def then_unreferenced_files_not_found(context): - execute_unreferenced_files(context) - assert int(context.exit_code) == 0 - assert context.stdout == "".encode('utf-8') +def assert_no_output(result): + assert result.stdout == "", "Expected standard output to be empty.\n" + \ + f"Standard output = {result.stdout.encode()}.\n" -@then('the unreferenced files are "{unreferenced_files}".') -def then_unreferenced_files_found(context, unreferenced_files): - execute_unreferenced_files(context) - unreferenced_files = unreferenced_files.strip() \ - .strip('\"').replace("\\n", '\n').encode('utf-8') - assert context.stdout == unreferenced_files - - -@then('the status code is nonzero.') -def then_nonzero_status_code(context): - if not hasattr(context, 'exit_code'): - execute_unreferenced_files(context) - assert int(context.exit_code) != 0 - - -@then('printed is an error message detailing that the argument search is missing.') -def then_search_argument_missing_error(context): - execute_unreferenced_files(context) - assert starts_with(context.stdout, MISSING_SEARCH) - then_nonzero_status_code(context) +def assert_no_errors(result): + assert result.stderr == "", "Expected standard error to be empty.\n" + \ + f"Standard error = {result.stderr.encode()}.\n" -@then('printed is an error message detailing that the argument search for is missing.') -def then_search_for_argument_missing_error(context): - execute_unreferenced_files(context) - assert starts_with(context.stdout, MISSING_SEARCH_FOR) - then_nonzero_status_code(context) +def assert_error_equals(result, error): + assert result.stderr == error, "Expected standard error to equal the error.\n" + \ + f"Standard error = {result.stderr.encode()}.\n" + \ + f"Error = {error.encode()}.\n" -@then('printed is an error message detailing that the arguments ignore and only search are mutually exclusive.') -def then_only_and_ignore_search_mutually_exclusive(context): - execute_unreferenced_files(context) - assert starts_with( - context.stdout, - ONLY_SEARCH_MUTUALLY_EXCLUSIVE) or starts_with( - context.stdout, - IGNORE_SEARCH_MUTUALLY_EXCLUSIVE) - then_nonzero_status_code(context) +def assert_error_is_one_of(result, errors): + assert result.stderr in errors, "Expected standard error to equal one of these errors.\n" + \ + f"Standard error = {result.stderr.encode()}.\n" + \ + f"Errors = {errors}.\n" -@then('printed is an error message detailing that the arguments ignore and only search for are mutually exclusive.') -def then_only_and_ignore_search_for_mutually_exclusive(context): - execute_unreferenced_files(context) - assert starts_with( - context.stdout, - ONLY_SEARCH_FOR_MUTUALLY_EXCLUSIVE) or starts_with( - context.stdout, - IGNORE_SEARCH_FOR_MUTUALLY_EXCLUSIVE) - then_nonzero_status_code(context) - - -def execute_unreferenced_files(context): - os.chdir(context.temporary_directory.name) - (context.exit_code, context.stdout) = execute_command( - context.pre_command + context.unreferenced_files_path + context.arguments) - os.chdir(context.behave_directory) - - -def starts_with(stdout, error_message): - return stdout.strip().startswith(bytes(error_message.strip(), 'utf-8')) +def assert_unreferenced_files(result, unreferenced_files): + unreferenced_files = unreferenced_files.strip() \ + .strip('\"') \ + .replace("\\n", '\n') + assert result.stdout == unreferenced_files, "The unreferenced files was not what was expected.\n" +\ + f"Expected =\n {unreferenced_files}.\n" + \ + f"Actual = \n {result.stdout}\n" diff --git a/end-to-end-tests/features/steps/git.py b/end-to-end-tests/features/steps/git.py deleted file mode 100644 index 981b5a3..0000000 --- a/end-to-end-tests/features/steps/git.py +++ /dev/null @@ -1,28 +0,0 @@ -import os -import tempfile -from behave import given -from util import execute_command - - -def reset_context(context): - context.behave_directory = os.getcwd() - context.temporary_directory = tempfile.TemporaryDirectory() - - context.pre_command = "" - context.unreferenced_files_path = context.behave_directory + \ - "/../target/debug/unreferenced_files" - context.arguments = "" - - -@given('the repository "{remote_repository}" is cloned and checked out at the commit "{commit_hash}".') -def clone_remote_repository_and_checkout_commit( - context, remote_repository, commit_hash): - reset_context(context) - - os.chdir(context.temporary_directory.name) - (exit_code, _) = execute_command( - "git clone --depth 1 " + remote_repository + " .") - assert exit_code == 0 - (exit_code, _) = execute_command("git fetch --depth 1 origin " + commit_hash) - assert exit_code == 0 - os.chdir(context.behave_directory) diff --git a/end-to-end-tests/features/steps/given.py b/end-to-end-tests/features/steps/given.py new file mode 100644 index 0000000..e1c422c --- /dev/null +++ b/end-to-end-tests/features/steps/given.py @@ -0,0 +1,33 @@ +import os +import hashlib +from behave import given + +from utilities import execute_command +from assertions import assert_command_successful + + +def reset_context(context): + context.behave_directory = os.getcwd() + + context.pre_command = "" + context.unreferenced_files_path = f"{context.behave_directory}/../target/debug/unreferenced_files" # fmt: off + context.arguments = "" + + +@given('the repository "{remote_repository}" is cloned and checked out at the commit "{commit_hash}".') +def clone_remote_repository_and_checkout_commit(context, remote_repository, commit_hash): + reset_context(context) + + md5 = hashlib.md5(remote_repository.encode()).hexdigest() + context.remote_repository_cache = f"/tmp/{md5}/{commit_hash}" + + if not os.path.exists(context.remote_repository_cache): + result = execute_command(f"git clone {remote_repository} {context.remote_repository_cache}") + assert_command_successful(result) + + os.chdir(context.remote_repository_cache) + + result = execute_command(f"git checkout {commit_hash}") + assert_command_successful(result) + + os.chdir(context.behave_directory) diff --git a/end-to-end-tests/features/steps/then.py b/end-to-end-tests/features/steps/then.py new file mode 100644 index 0000000..8165ec0 --- /dev/null +++ b/end-to-end-tests/features/steps/then.py @@ -0,0 +1,96 @@ +from behave import then + +from utilities import execute_unreferenced_files +from assertions import ( + assert_no_output, + assert_no_errors, + assert_command_successful, + assert_command_unsuccessful, + assert_error_equals, + assert_error_is_one_of, + assert_unreferenced_files, +) + + +@then('unreferenced files are not found.') +def assert_unreferenced_files_not_found(context): + # When + result = execute_unreferenced_files(context) + + # Then + assert_no_output(result) + assert_no_errors(result) + assert_command_successful(result) + return result + + +@then('the unreferenced files are "{unreferenced_files}".') +def assert_unreferenced_files_found(context, unreferenced_files): + # When/Then + result = assert_unreferenced_files_fails(context) + + # Then + assert_unreferenced_files(result, unreferenced_files) + + +def assert_unreferenced_files_fails(context): + # When + result = execute_unreferenced_files(context) + + # Then + assert_command_unsuccessful(result) + return result + + +@then('printed is an error message detailing that the argument search is missing.') +def assert_search_argument_missing_error(context): + # Given + search_argument_missing_error = "error: the following required arguments were not provided:\n --search \n\nUsage: unreferenced_files --search-for --search \n\nFor more information, try '--help'.\n" + + # When/Then + result = assert_unreferenced_files_fails(context) + + # Then + assert_error_equals(result, search_argument_missing_error) + + +@then('printed is an error message detailing that the argument search for is missing.') +def assert_search_for_argument_missing_error(context): + # Given + search_for_argument_missing_error = "error: the following required arguments were not provided:\n --search-for \n\nUsage: unreferenced_files --search-for --search \n\nFor more information, try '--help'.\n" + + # When/Then + result = assert_unreferenced_files_fails(context) + + # Then + assert_error_equals(result, search_for_argument_missing_error) + + +@then('printed is an error message detailing that the arguments ignore and only search are mutually exclusive.') +def assert_only_and_ignore_search_mutually_exclusive_error(context): + # Given + only_search_mutually_exclusive_error = "error: the argument '--only-search ' cannot be used with '--ignore-search '\n\nUsage: unreferenced_files --search-for --search --only-search \n\nFor more information, try '--help'.\n" + ignore_search_mutually_exclusive_error = "error: the argument '--ignore-search ' cannot be used with '--only-search '\n\nUsage: unreferenced_files --search-for --search --ignore-search \n\nFor more information, try '--help'.\n" + + # When/Then + result = assert_unreferenced_files_fails(context) + + # Then + assert_error_is_one_of(result, + [only_search_mutually_exclusive_error, + ignore_search_mutually_exclusive_error]) + + +@then('printed is an error message detailing that the arguments ignore and only search for are mutually exclusive.') +def assert_only_and_ignore_search_for_mutually_exclusive_error(context): + # Given + only_search_for_mutually_exclusive_error = "error: the argument '--only-search-for ' cannot be used with '--ignore-search-for '\n\nUsage: unreferenced_files --search-for --search --only-search-for \n\nFor more information, try '--help'.\n" + ignore_search_for_mutually_exclusive_error = "error: the argument '--ignore-search-for ' cannot be used with '--only-search-for '\n\nUsage: unreferenced_files --search-for --search --ignore-search-for \n\nFor more information, try '--help'.\n" + + # When/Then + result = assert_unreferenced_files_fails(context) + + # Then + assert_error_is_one_of(result, + [only_search_for_mutually_exclusive_error, + ignore_search_for_mutually_exclusive_error]) diff --git a/end-to-end-tests/features/steps/util.py b/end-to-end-tests/features/steps/util.py deleted file mode 100644 index ab1d3de..0000000 --- a/end-to-end-tests/features/steps/util.py +++ /dev/null @@ -1,13 +0,0 @@ -from subprocess import Popen, PIPE, STDOUT - - -def execute_command(command): - process = Popen( - command, - shell=True, - stdin=PIPE, - stdout=PIPE, - stderr=STDOUT) - process.wait() - - return process.returncode, process.stdout.read() diff --git a/end-to-end-tests/features/steps/utilities.py b/end-to-end-tests/features/steps/utilities.py new file mode 100644 index 0000000..b458616 --- /dev/null +++ b/end-to-end-tests/features/steps/utilities.py @@ -0,0 +1,37 @@ +import os + +from subprocess import Popen, PIPE + + +def execute_command(command): + process = Popen( + command, + shell=True, + stdin=PIPE, + stdout=PIPE, + stderr=PIPE) + process.wait() + + result = type("Result", (), {}) + result.exit_code = process.returncode + + stdout, stderr = process.communicate() + result.stdout = stdout.decode("utf-8") + result.stderr = stderr.decode("utf-8") + + return result + + +def execute_unreferenced_files(context): + if "GIT_DIR" not in os.environ: + os.chdir(context.remote_repository_cache) + + result = execute_command( + context.pre_command + + context.unreferenced_files_path + + context.arguments) + + if "GIT_DIR" not in os.environ: + os.chdir(context.behave_directory) + + return result diff --git a/end-to-end-tests/features/steps/arguments.py b/end-to-end-tests/features/steps/when.py similarity index 57% rename from end-to-end-tests/features/steps/arguments.py rename to end-to-end-tests/features/steps/when.py index fee0501..b01ec57 100644 --- a/end-to-end-tests/features/steps/arguments.py +++ b/end-to-end-tests/features/steps/when.py @@ -1,4 +1,4 @@ -from behave import * +from behave import when @when('the flag --assert-no-unreferenced-files is set.') @@ -7,35 +7,35 @@ def set_assert_no_unreferenced_files(context): @when('the flag --only-file-name is set.') -def set_assert_no_unreferenced_files(context): +def set_only_file_name(context): context.arguments += " --only-file-name " @when('the argument --search-for is provided as "{search_for}".') def set_search_for(context, search_for): - context.arguments += " --search-for " + search_for + " " + context.arguments += f" --search-for {search_for} " @when('the argument --only-search-for is provided as "{only_search_for}".') -def set_only_search(context, only_search_for): - context.arguments += " --only-search-for " + only_search_for + " " +def set_only_search_for(context, only_search_for): + context.arguments += f" --only-search-for {only_search_for} " @when('the argument --ignore-search-for is provided as "{ignore_search_for}".') -def set_only_search(context, ignore_search_for): - context.arguments += " --ignore-search-for " + ignore_search_for + " " +def set_ignore_search_for(context, ignore_search_for): + context.arguments += f" --ignore-search-for {ignore_search_for} " @when('the argument --search is provided as "{search}".') def set_search(context, search): - context.arguments += " --search " + search + " " + context.arguments += f" --search {search} " @when('the argument --only-search is provided as "{only_search}".') def set_only_search(context, only_search): - context.arguments += " --only-search " + only_search + " " + context.arguments += f" --only-search {only_search} " @when('the argument --ignore-search is provided as "{ignore_search}".') -def set_only_search(context, ignore_search): - context.arguments += " --ignore-search " + ignore_search + " " +def set_ignore_search(context, ignore_search): + context.arguments += f" --ignore-search {ignore_search} " diff --git a/end-to-end-tests/requirements.txt b/end-to-end-tests/requirements.txt deleted file mode 100644 index f250430..0000000 --- a/end-to-end-tests/requirements.txt +++ /dev/null @@ -1,4 +0,0 @@ -behave==1.2.6 -parse==1.19.0 -parse-type==0.5.2 -six==1.15.0 diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..533636e --- /dev/null +++ b/flake.lock @@ -0,0 +1,82 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1778274207, + "narHash": "sha256-I4puXmX1iovcCHZlRmztO3vW0mAbbRvq4F8wgIMQ1MM=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "b3da656039dc7a6240f27b2ef8cc6a3ef3bccae7", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs", + "rust-overlay": "rust-overlay" + } + }, + "rust-overlay": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1778383025, + "narHash": "sha256-UK7s2LJS1YwIMFL7PSaNJvLXT9pyRgm7X+HNPgMXiEE=", + "owner": "oxalica", + "repo": "rust-overlay", + "rev": "4568a557ca325ff81fb354382d4a9968daa1001a", + "type": "github" + }, + "original": { + "owner": "oxalica", + "repo": "rust-overlay", + "type": "github" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..d0373fb --- /dev/null +++ b/flake.nix @@ -0,0 +1,66 @@ +{ + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; + + rust-overlay = { + url = "github:oxalica/rust-overlay"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + + flake-utils.url = "github:numtide/flake-utils"; + }; + + outputs = { self, nixpkgs, rust-overlay, flake-utils }: + flake-utils.lib.eachDefaultSystem (system: + let + overlays = [ (import rust-overlay) ]; + pkgs = import nixpkgs { + inherit system overlays; + }; + + # Architecture-specific target + rustTarget = if pkgs.stdenv.isAarch64 + then "aarch64-unknown-linux-musl" + else "x86_64-unknown-linux-musl"; + + rustWithTargets = pkgs.rust-bin.stable.latest.default.override { + targets = [ rustTarget ]; + }; + in + { + devShells.default = pkgs.mkShell { + # Disable all Nix hardening flags to prevent interference with Cargo builds. + # These flags are designed for C/C++ and can cause issues with: + # - MUSL builds (fortify adds glibc-specific functions) + # - Crates that vendor C libraries (e.g., git2 vendoring libgit2) + # Rust already provides memory safety, so these hardening flags provide + # minimal benefit while causing build problems. + hardeningDisable = [ "all" ]; + + buildInputs = [ + # Rust with cross-compilation targets built-in. + rustWithTargets + # Shell formatting. + pkgs.shfmt + # Python formatting. + pkgs.python313Packages.autopep8 + # YAML formatting. + pkgs.yamlfmt + # Shell linting. + pkgs.shellcheck + # Python linting. + pkgs.ruff + # GitHub Actions workflows linting. + pkgs.actionlint + # Rust dependencies check. + pkgs.cargo-machete + # End to end tests. + pkgs.python313 + pkgs.python313Packages.behave + # Deploying. + pkgs.gh + ]; + }; + } + ); +} diff --git a/release-please-config.json b/release-please-config.json new file mode 100644 index 0000000..f06761c --- /dev/null +++ b/release-please-config.json @@ -0,0 +1,13 @@ +{ + "changelog-type": "github", + "release-type": "rust", + "pull-request-title-pattern": "chore: release ${version}", + "include-component-in-tag": false, + "packages": { + ".": { + "extra-files": [ + "README.md" + ] + } + } +} diff --git a/src/cli/mod.rs b/src/cli/mod.rs index 2059910..56a8868 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -1,88 +1,88 @@ -use structopt::{clap::ArgGroup, StructOpt}; +use clap::Parser; -#[derive(Debug, StructOpt)] -#[structopt( - name = "unreferenced_files", - about = "A utility for finding unused and unreferenced files.", - group = ArgGroup::with_name("only").required(false), - group = ArgGroup::with_name("search_filters").required(false), - group = ArgGroup::with_name("search_for_filters").required(false), -)] -pub struct Arguments { - #[structopt( +#[derive(Parser, Debug)] +#[command(author, version, about, long_about = None)] +pub(crate) struct Arguments { + #[arg( long, required = true, - min_values = 1, + num_args = 1, help = "Search for references of this file or if it is a directory all resources within, multiple resources can be provided." )] - pub search_for: Vec, + pub(crate) search_for: Vec, - #[structopt( + #[arg( long, group = "search_for_filters", help = "Only search for files that match any of these regexes, mutually exclusive with ignore search for." )] - pub only_search_for: Vec, + pub(crate) only_search_for: Vec, - #[structopt( + #[arg( long, group = "search_for_filters", help = "Ignore and do not search for any files that match any of these regexes, mutually exclusive with only search for." )] - pub ignore_search_for: Vec, + pub(crate) ignore_search_for: Vec, - #[structopt( + #[arg( long, required = true, - min_values = 1, + num_args = 1, help = "Search this file or if it is a directory all resources within for references, multiple resources can be provided." )] - pub search: Vec, + pub(crate) search: Vec, - #[structopt( + #[arg( long, group = "search_filters", help = "Only search files that match any of these regexes, mutual exclusive with ignore search." )] - pub only_search: Vec, + pub(crate) only_search: Vec, - #[structopt( + #[arg( long, group = "search_filters", help = "Ignore and do not search any files that match any of these regexes, mutually exclusive with only search." )] - pub ignore_search: Vec, + pub(crate) ignore_search: Vec, - #[structopt( + #[arg( long, group = "only", help = "Only search for unreferenced files via their file name, mutually exclusive with other only flags." )] - pub only_file_name: bool, + pub(crate) only_file_name: bool, - #[structopt( + #[arg( long, group = "only", help = "Only search for unreferenced files via their file name without the extension, mutually exclusive with other only flags." )] - pub only_file_stem: bool, + pub(crate) only_file_stem: bool, - #[structopt( + #[arg( long, group = "only", help = "Only search for unreferenced files via their relative path, mutually exclusive with other only flags." )] - pub only_relative_path: bool, + pub(crate) only_relative_path: bool, - #[structopt( + #[arg( long, help = "Output the full path of each unreferenced file, instead of the relative path." )] - pub print_full_path: bool, + pub(crate) print_full_path: bool, - #[structopt( + #[arg( long, help = "Return a nonzero exit code if there are any unreferenced files." )] - pub assert_no_unreferenced_files: bool, + pub(crate) assert_no_unreferenced_files: bool, + + #[arg( + long, + help = "Enable verbose output, respects RUST_LOG environment variable if set." + )] + pub(crate) verbose: bool, } diff --git a/src/file_path_variants/mod.rs b/src/file_path_variants/mod.rs new file mode 100644 index 0000000..0516bd7 --- /dev/null +++ b/src/file_path_variants/mod.rs @@ -0,0 +1,105 @@ +use std::cmp::Ordering; +use std::hash::{Hash, Hasher}; +use std::path::{Path, PathBuf}; + +use anyhow::{bail, Result}; + +#[derive(Debug, Clone, Eq)] +pub struct FilePathVariants { + pub(crate) file_canonicalize_path: String, + pub(crate) file_relative_path: String, + pub(crate) file_name: String, + pub(crate) file_stem: String, +} + +impl FilePathVariants { + pub(crate) fn new(path: PathBuf) -> Result { + fn get_file_canonicalize_path(path: &Path) -> Result { + let canonicalized_path = path.canonicalize()?; + Ok(canonicalized_path.display().to_string()) + } + + fn get_file_relative_path(path: &Path) -> String { + path.display() + .to_string() + .trim_start_matches("./") + .to_string() + } + + fn get_file_name(path: &Path) -> Result { + match path.file_name() { + Some(file_name) => match file_name.to_str() { + Some(str) => Ok(str.to_string()), + None => { + bail!(format!("Can not convert {:?} to str.", file_name)); + } + }, + None => { + bail!(format!("{:?} has no file name.", path.display())); + } + } + } + + fn get_file_stem(path: &Path) -> Result { + match path.file_stem() { + Some(file_stem) => match file_stem.to_str() { + Some(str) => Ok(str.to_string()), + None => { + bail!(format!("Can not convert {:?} to str.", file_stem)); + } + }, + None => { + bail!(format!("{:?} has no file steam.", path.display())); + } + } + } + + let file_canonicalize_path = get_file_canonicalize_path(&path)?; + let file_name = get_file_name(&path)?; + let file_stem = get_file_stem(&path)?; + + Ok(FilePathVariants { + file_canonicalize_path, + file_relative_path: get_file_relative_path(&path), + file_name, + file_stem, + }) + } + + pub fn print(&self, print_full_path: bool) { + if print_full_path { + println!("{}", self.file_canonicalize_path); + } else { + println!("{}", self.file_relative_path); + } + } +} + +impl PartialEq for FilePathVariants { + #[inline] + fn eq(&self, other: &FilePathVariants) -> bool { + self.file_canonicalize_path == other.file_canonicalize_path + } +} + +impl Hash for FilePathVariants { + #[inline] + fn hash(&self, hasher: &mut H) { + self.file_canonicalize_path.hash(hasher); + } +} + +impl PartialOrd for FilePathVariants { + #[inline] + fn partial_cmp(&self, other: &FilePathVariants) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for FilePathVariants { + #[inline] + fn cmp(&self, other: &FilePathVariants) -> Ordering { + self.file_canonicalize_path + .cmp(&other.file_canonicalize_path) + } +} diff --git a/src/file_path_variants_regexes/mod.rs b/src/file_path_variants_regexes/mod.rs new file mode 100644 index 0000000..f7e18bc --- /dev/null +++ b/src/file_path_variants_regexes/mod.rs @@ -0,0 +1,76 @@ +use std::collections::{HashMap, HashSet}; + +use regex::Regex; + +use crate::file_path_variants::FilePathVariants; + +pub(crate) struct FilePathVariantsRegexes { + file_path_variants_regexes: HashMap, +} + +impl FilePathVariantsRegexes { + pub(crate) fn new( + files: &HashSet, + search_for_relative_path: bool, + search_for_file_name: bool, + search_for_file_steam: bool, + ) -> Result { + let mut file_path_variants_regexes = HashMap::new(); + + for file in files { + if search_for_relative_path { + match Regex::new(&file.file_relative_path) { + Ok(relative_path_regex) => { + file_path_variants_regexes + .insert(file.file_relative_path.clone(), relative_path_regex); + } + Err(_) => { + error!( + "Unable to create a regex from the file relative path {:?}.", + file.file_relative_path + ); + return Err(()); + } + } + } + + if search_for_file_name { + match Regex::new(&file.file_name) { + Ok(file_name_regex) => { + file_path_variants_regexes.insert(file.file_name.clone(), file_name_regex); + } + Err(_) => { + error!( + "Unable to create a regex from the file name {:?}.", + file.file_relative_path + ); + return Err(()); + } + } + } + + if search_for_file_steam { + match Regex::new(&file.file_stem) { + Ok(file_stem_regex) => { + file_path_variants_regexes.insert(file.file_stem.clone(), file_stem_regex); + } + Err(_) => { + error!( + "Unable to create a regex from the file stem {:?}.", + file.file_relative_path + ); + return Err(()); + } + } + } + } + + Ok(FilePathVariantsRegexes { + file_path_variants_regexes, + }) + } + + pub(crate) fn get(&self, file_path: &str) -> &Regex { + self.file_path_variants_regexes.get(file_path).unwrap() + } +} diff --git a/src/file_utilities/mod.rs b/src/file_utilities/mod.rs deleted file mode 100644 index 3f6bee8..0000000 --- a/src/file_utilities/mod.rs +++ /dev/null @@ -1,41 +0,0 @@ -use std::path::{Path, PathBuf}; -use std::process::exit; - -pub fn get_paths(paths: Vec) -> Vec { - paths.iter().map(|path| get_path(path)).collect() -} - -fn get_path(path: &str) -> PathBuf { - let path = Path::new(path); - - if !path.exists() { - error!("{:?} does not exist.", path); - exit(crate::ERROR_EXIT_CODE); - } - - path.to_path_buf() -} - -pub fn get_file_content(path: &Path) -> Option { - match std::fs::read_to_string(path) { - Ok(file_content) => Some(file_content), - Err(error) => { - warn!( - "Encountered {} while trying to read the file {}.", - error, - path.display() - ); - None - } - } -} - -pub fn get_directory_entries(path: &Path) -> std::fs::ReadDir { - match std::fs::read_dir(path) { - Ok(entries) => entries, - Err(error) => { - error!("{:?}", error); - exit(crate::ERROR_EXIT_CODE); - } - } -} diff --git a/src/filters/mod.rs b/src/filters/mod.rs new file mode 100644 index 0000000..f634401 --- /dev/null +++ b/src/filters/mod.rs @@ -0,0 +1,74 @@ +use anyhow::{bail, Context, Result}; +use regex::Regex; + +pub struct Filters { + filters: Vec, + filtering_on: FilteringOn, +} + +enum FilteringOn { + Only, + Ignore, + None, +} + +impl Filters { + pub fn new(only_search: Vec, ignore_search: Vec) -> Result { + fn to_regexes(to_regexes: Vec) -> Result> { + let mut regexes = vec![]; + + for to_regex in to_regexes { + let regex = Regex::new(&to_regex) + .context(format!("Unable to convert {to_regex:?} to a regex."))?; + regexes.push(regex); + } + + Ok(regexes) + } + + match (only_search.len(), ignore_search.len()) { + (0, 0) => Ok(Filters { + filters: vec![], + filtering_on: FilteringOn::None, + }), + (_, 0) => { + let filters = + to_regexes(only_search).context("Unable to create only search filters.")?; + Ok(Filters { + filters, + filtering_on: FilteringOn::Only, + }) + } + + (0, _) => { + let filters = + to_regexes(ignore_search).context("Unable to create ignore search filters.")?; + Ok(Filters { + filters, + filtering_on: FilteringOn::Ignore, + }) + } + _ => { + bail!("Only and ignore filters are mutually exclusive."); + } + } + } + + pub(crate) fn should_ignore(&self, file_canonicalize_path: &str) -> bool { + fn matches_any(checking: &str, regexes: &[Regex]) -> bool { + for regex in regexes { + if regex.is_match(checking) { + return false; + } + } + + true + } + + match self.filtering_on { + FilteringOn::Only => matches_any(file_canonicalize_path, &self.filters), + FilteringOn::Ignore => !matches_any(file_canonicalize_path, &self.filters), + FilteringOn::None => false, + } + } +} diff --git a/src/main.rs b/src/main.rs index 19d8cac..30d2676 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,59 +1,78 @@ #[macro_use] extern crate log; extern crate pretty_env_logger; -extern crate regex; -use std::process::exit; +#[cfg(test)] +#[macro_use] +extern crate lazy_static; -use structopt::StructOpt; +use anyhow::{bail, Result}; +use clap::Parser; + +use crate::cli::Arguments; +use crate::filters::Filters; +use crate::search::Search; +use crate::search_for::SearchFor; mod cli; -mod file_utilities; -mod model; -mod regex_utilities; +mod file_path_variants; +mod file_path_variants_regexes; +mod filters; mod reporter; +mod search; +mod search_for; +mod utilities; + +#[cfg(test)] +mod tests; +// TODO Assert failed vs failed to do something error status codes. const ERROR_EXIT_CODE: i32 = 1; fn main() { + let arguments = cli::Arguments::parse(); + + // Set up logging: if verbose is true and RUST_LOG is not set, default to info level + if arguments.verbose && std::env::var("RUST_LOG").is_err() { + std::env::set_var("RUST_LOG", "info"); + } + pretty_env_logger::init(); - let arguments = cli::Arguments::from_args(); - debug!("The command line arguments provided are {:?}.", arguments); + info!("Version {}.", env!("CARGO_PKG_VERSION")); + debug!("The command line arguments provided are {arguments:?}."); + + if let Err(err) = run(arguments) { + error!("{err:?}"); + std::process::exit(ERROR_EXIT_CODE); + } +} + +fn run(arguments: Arguments) -> Result<()> { let search_for_relative_path = !arguments.only_file_name && !arguments.only_file_stem; let search_for_file_name = !arguments.only_relative_path && !arguments.only_file_stem; let search_for_file_stem = !arguments.only_relative_path && !arguments.only_file_name; - let search_for_filters = - crate::model::filters::Filters::new(arguments.only_search_for, arguments.ignore_search_for); - let mut unreferenced_files = crate::model::unreferenced_files::UnreferencedFiles::new( - file_utilities::get_paths(arguments.search_for), - search_for_filters, - ); + let search_for_fitlers = Filters::new(arguments.only_search_for, arguments.ignore_search_for)?; + let search_for = SearchFor::new(&arguments.search_for, search_for_fitlers)?; - let search_filters = - crate::model::filters::Filters::new(arguments.only_search, arguments.ignore_search); - let search = crate::model::raw_files::RawFiles::new( - file_utilities::get_paths(arguments.search), - search_filters, - ); + let search_filters = Filters::new(arguments.only_search, arguments.ignore_search)?; + let search = Search::new(&arguments.search, search_filters)?; - unreferenced_files.remove_referenced_files( + let unreferenced_files = search_for.get_unreferenced_files( search, search_for_relative_path, search_for_file_name, search_for_file_stem, ); - let is_not_empty = !unreferenced_files.is_empty(); + let is_unreferenced_files = !unreferenced_files.is_empty(); crate::reporter::print(unreferenced_files, arguments.print_full_path); - if arguments.assert_no_unreferenced_files && is_not_empty { - exit(ERROR_EXIT_CODE); + if arguments.assert_no_unreferenced_files && is_unreferenced_files { + bail!("There are unreferenced files.") } -} -#[cfg(test)] -#[macro_use] -extern crate lazy_static; + Ok(()) +} diff --git a/src/model/file_path_variants/mod.rs b/src/model/file_path_variants/mod.rs deleted file mode 100644 index 2497b6d..0000000 --- a/src/model/file_path_variants/mod.rs +++ /dev/null @@ -1,70 +0,0 @@ -use std::path::{Path, PathBuf}; -use std::process::exit; - -#[derive(Debug, Hash, Clone, PartialOrd, PartialEq, Ord, Eq)] -pub struct FilePathVariants { - pub file_canonicalize_path: String, - pub file_relative_path: String, - pub file_name: String, - pub file_stem: String, -} - -impl FilePathVariants { - pub fn new(path: PathBuf) -> Self { - fn get_file_canonicalize_path(path: &Path) -> String { - match path.canonicalize() { - Ok(canonicalized_path) => canonicalized_path.display().to_string(), - Err(error) => { - error!("{:?}", error); - exit(crate::ERROR_EXIT_CODE); - } - } - } - - fn get_file_relative_path(path: &Path) -> String { - path.display() - .to_string() - .trim_start_matches("./") - .to_string() - } - - fn get_file_name(path: &Path) -> String { - match path.file_name() { - Some(file_name) => match file_name.to_str() { - Some(str) => str.to_string(), - None => { - error!("Can not convert {:?} to str.", file_name); - exit(crate::ERROR_EXIT_CODE); - } - }, - None => { - error!("{:?} has no file name.", path.display()); - exit(crate::ERROR_EXIT_CODE); - } - } - } - - fn get_file_stem(path: &Path) -> String { - match path.file_stem() { - Some(file_stem) => match file_stem.to_str() { - Some(str) => str.to_string(), - None => { - error!("Can not convert {:?} to str.", file_stem); - exit(crate::ERROR_EXIT_CODE); - } - }, - None => { - error!("{:?} has no file steam.", path.display()); - exit(crate::ERROR_EXIT_CODE); - } - } - } - - FilePathVariants { - file_canonicalize_path: get_file_canonicalize_path(&path), - file_relative_path: get_file_relative_path(&path), - file_name: get_file_name(&path), - file_stem: get_file_stem(&path), - } - } -} diff --git a/src/model/file_path_variants_regexes/mod.rs b/src/model/file_path_variants_regexes/mod.rs deleted file mode 100644 index 3d6e554..0000000 --- a/src/model/file_path_variants_regexes/mod.rs +++ /dev/null @@ -1,67 +0,0 @@ -use std::collections::{HashMap, HashSet}; - -use regex::Regex; - -use crate::model::file_path_variants::FilePathVariants; -use crate::model::raw_file::RawFile; -use crate::regex_utilities::get_regex; - -pub struct FilePathVariantsRegexes { - file_path_variants_regexes: HashMap, -} - -impl FilePathVariantsRegexes { - pub fn new( - files: &HashSet, - search_for_relative_path: bool, - search_for_file_name: bool, - search_for_file_steam: bool, - ) -> FilePathVariantsRegexes { - let mut file_path_variants_regexes = HashMap::new(); - - for file in files { - if search_for_relative_path { - let relative_path_regex = get_regex(&file.file_relative_path); - file_path_variants_regexes - .insert(file.file_relative_path.clone(), relative_path_regex); - } - - if search_for_file_name { - let file_name_regex = get_regex(&file.file_name); - file_path_variants_regexes.insert(file.file_name.clone(), file_name_regex); - } - - if search_for_file_steam { - let file_stem_regex = get_regex(&file.file_stem); - file_path_variants_regexes.insert(file.file_stem.clone(), file_stem_regex); - } - } - - FilePathVariantsRegexes { - file_path_variants_regexes, - } - } - - pub fn is_file_path_in_file( - &self, - file_path_variant_searching_for: &str, - searching: &RawFile, - ) -> bool { - match self - .file_path_variants_regexes - .get(file_path_variant_searching_for) - .unwrap() - .is_match(&searching.file_content) - { - true => { - trace!( - "Found the text {:?} inside the file {:?}.", - file_path_variant_searching_for, - searching.file_path_variants.file_relative_path - ); - true - } - false => false, - } - } -} diff --git a/src/model/filters/mod.rs b/src/model/filters/mod.rs deleted file mode 100644 index 8efbd16..0000000 --- a/src/model/filters/mod.rs +++ /dev/null @@ -1,61 +0,0 @@ -use std::process::exit; - -use regex::Regex; - -pub struct Filters { - filters: Vec, - filtering_on: FilteringOn, -} - -enum FilteringOn { - Only, - Ignore, - None, -} - -impl Filters { - pub fn new(only_search: Vec, ignore_search: Vec) -> Self { - return match (only_search.len(), ignore_search.len()) { - (0, 0) => Filters { - filters: vec![], - filtering_on: FilteringOn::None, - }, - (_, 0) => Filters { - filters: only_search - .iter() - .map(|regex| crate::regex_utilities::get_regex(regex)) - .collect(), - filtering_on: FilteringOn::Only, - }, - (0, _) => Filters { - filters: ignore_search - .iter() - .map(|regex| crate::regex_utilities::get_regex(regex)) - .collect(), - filtering_on: FilteringOn::Ignore, - }, - _ => { - error!("Only and ignore filters are mutually exclusive."); - exit(crate::ERROR_EXIT_CODE); - } - }; - } - - pub fn is_filtered_out(&self, file_canonicalize_path: &str) -> bool { - fn matches_any(checking: &str, regexes: &[Regex]) -> bool { - for regex in regexes { - if regex.is_match(checking) { - return false; - } - } - - true - } - - match self.filtering_on { - FilteringOn::Only => matches_any(file_canonicalize_path, &self.filters), - FilteringOn::Ignore => !matches_any(file_canonicalize_path, &self.filters), - FilteringOn::None => false, - } - } -} diff --git a/src/model/mod.rs b/src/model/mod.rs deleted file mode 100644 index 243efee..0000000 --- a/src/model/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -pub mod file_path_variants; -pub mod file_path_variants_regexes; -pub mod filters; -pub mod raw_file; -pub mod raw_files; -pub mod unreferenced_files; diff --git a/src/model/raw_file/mod.rs b/src/model/raw_file/mod.rs deleted file mode 100644 index 2d2ce92..0000000 --- a/src/model/raw_file/mod.rs +++ /dev/null @@ -1,24 +0,0 @@ -use std::path::PathBuf; - -use crate::model::file_path_variants::FilePathVariants; - -pub type FileContent = String; - -#[derive(Hash, Clone, PartialOrd, PartialEq, Ord, Eq)] -pub struct RawFile { - pub file_path_variants: FilePathVariants, - pub file_content: FileContent, -} - -impl RawFile { - pub fn new(path: PathBuf) -> Option { - if let Some(file_content) = crate::file_utilities::get_file_content(&path) { - Some(RawFile { - file_path_variants: crate::model::file_path_variants::FilePathVariants::new(path), - file_content, - }) - } else { - None - } - } -} diff --git a/src/model/raw_files/mod.rs b/src/model/raw_files/mod.rs deleted file mode 100644 index 6c362af..0000000 --- a/src/model/raw_files/mod.rs +++ /dev/null @@ -1,81 +0,0 @@ -use std::collections::HashSet; -use std::path::{Path, PathBuf}; -use std::process::exit; - -use crate::model::filters::Filters; -use crate::model::raw_file::RawFile; - -pub struct RawFiles { - pub raw_files: HashSet, -} - -impl RawFiles { - pub fn new(paths: Vec, filters: Filters) -> Self { - fn get_raw_files_in_directory(path: &Path, filters: &Filters) -> HashSet { - let mut raw_files = HashSet::new(); - trace!( - "Searching the directory {:?} for files to search.", - path.display() - ); - - for dir_entry in crate::file_utilities::get_directory_entries(path) { - match dir_entry { - Ok(dir_entry) => { - let path = dir_entry.path(); - - if path.is_file() { - if let Some(raw_file) = get_raw_file(path, filters) { - raw_files.insert(raw_file); - } - } else { - raw_files.extend(get_raw_files_in_directory(path.as_path(), filters)); - } - } - Err(error) => { - error!("{:?}", error); - exit(crate::ERROR_EXIT_CODE); - } - } - } - - raw_files - } - - fn get_raw_file(path: PathBuf, filters: &Filters) -> Option { - if path.is_file() { - if let Some(raw_file) = RawFile::new(path) { - if filters.is_filtered_out(&&raw_file.file_path_variants.file_canonicalize_path) - { - debug!( - "Ignoring the file {:?} and not searching it.", - raw_file.file_path_variants.file_relative_path - ); - } else { - trace!( - "Adding {:?} to the files searching.", - raw_file.file_path_variants.file_relative_path - ); - return Some(raw_file); - } - } - } else { - error!("{:?} is not a file.", path); - exit(crate::ERROR_EXIT_CODE); - } - - None - } - - let mut raw_files = HashSet::new(); - - for path in paths { - if path.is_dir() { - raw_files.extend(get_raw_files_in_directory(&path, &filters)); - } else if let Some(raw_file) = get_raw_file(path.to_path_buf(), &filters) { - raw_files.insert(raw_file); - } - } - - RawFiles { raw_files } - } -} diff --git a/src/model/unreferenced_files/mod.rs b/src/model/unreferenced_files/mod.rs deleted file mode 100644 index 1553a8b..0000000 --- a/src/model/unreferenced_files/mod.rs +++ /dev/null @@ -1,162 +0,0 @@ -use std::collections::HashSet; -use std::path::{Path, PathBuf}; -use std::process::exit; - -use crate::model::file_path_variants::FilePathVariants; -use crate::model::file_path_variants_regexes::FilePathVariantsRegexes; -use crate::model::filters::Filters; -use crate::model::raw_files::RawFiles; - -#[derive(Clone)] -pub struct UnreferencedFiles { - pub unreferenced_files: HashSet, -} - -impl UnreferencedFiles { - pub fn new(search_for: Vec, filters: Filters) -> Self { - fn get_file_path_variants_in_directory( - path: &Path, - filters: &Filters, - ) -> HashSet { - let mut files_path_variants = HashSet::new(); - trace!( - "Searching the directory {:?} for files to search for.", - path.display() - ); - - for dir_entry in crate::file_utilities::get_directory_entries(path) { - match dir_entry { - Ok(dir_entry) => { - let path = dir_entry.path(); - - if path.is_file() { - if let Some(file_path_variants) = get_file_path_variants(path, filters) - { - files_path_variants.insert(file_path_variants); - } - } else { - files_path_variants.extend(get_file_path_variants_in_directory( - path.as_path(), - filters, - )); - } - } - Err(error) => { - error!("{:?}", error); - exit(crate::ERROR_EXIT_CODE); - } - } - } - - files_path_variants - } - - fn get_file_path_variants(path: PathBuf, filters: &Filters) -> Option { - if path.is_file() { - let file_path_variants = FilePathVariants::new(path); - - if filters.is_filtered_out(&&file_path_variants.file_canonicalize_path) { - debug!( - "Ignoring the file {:?} and not searching for it.", - file_path_variants.file_relative_path - ); - } else { - trace!( - "Adding {:?} to the files searching for.", - file_path_variants.file_relative_path - ); - return Some(file_path_variants); - } - } else { - error!("{:?} is not a file.", path); - exit(crate::ERROR_EXIT_CODE); - } - - None - } - - let mut unreferenced_files = HashSet::new(); - - for path in search_for { - if path.is_file() { - if let Some(file_path_variants) = - get_file_path_variants(path.to_path_buf(), &filters) - { - unreferenced_files.insert(file_path_variants); - } - } else { - unreferenced_files.extend(get_file_path_variants_in_directory(&path, &filters)); - } - } - - UnreferencedFiles { unreferenced_files } - } - - pub fn is_empty(&self) -> bool { - self.unreferenced_files.is_empty() - } - - pub fn remove_referenced_files( - &mut self, - search: RawFiles, - search_for_relative_path: bool, - search_for_file_name: bool, - search_for_file_stem: bool, - ) { - let file_path_variants_regexes = FilePathVariantsRegexes::new( - &self.unreferenced_files, - search_for_relative_path, - search_for_file_name, - search_for_file_stem, - ); - - for raw_file in search.raw_files { - if !self.unreferenced_files.is_empty() { - info!( - "Searching the file {:?}.", - raw_file.file_path_variants.file_relative_path - ); - - self.unreferenced_files.retain(|unreferenced_file| { - if unreferenced_file.file_canonicalize_path - == raw_file.file_path_variants.file_canonicalize_path - { - warn!( - "Not searching {:?} for {:?} as they are the same file.", - raw_file.file_path_variants.file_relative_path, - unreferenced_file.file_relative_path - ); - return true; - } - - if search_for_relative_path - && file_path_variants_regexes - .is_file_path_in_file(&unreferenced_file.file_relative_path, &raw_file) - { - return false; - } - - if search_for_file_name - && file_path_variants_regexes - .is_file_path_in_file(&unreferenced_file.file_name, &raw_file) - { - return false; - } - - if search_for_file_stem - && file_path_variants_regexes - .is_file_path_in_file(&unreferenced_file.file_stem, &raw_file) - { - return false; - } - - true - }); - } - } - } -} - -#[cfg(test)] -#[macro_use] -mod tests; diff --git a/src/model/unreferenced_files/tests/file_name/snapshots/unreferenced_files__model__unreferenced_files__tests__file_name__found.snap b/src/model/unreferenced_files/tests/file_name/snapshots/unreferenced_files__model__unreferenced_files__tests__file_name__found.snap deleted file mode 100644 index 1a5632d..0000000 --- a/src/model/unreferenced_files/tests/file_name/snapshots/unreferenced_files__model__unreferenced_files__tests__file_name__found.snap +++ /dev/null @@ -1,6 +0,0 @@ ---- -source: src/model/unreferenced_files/tests/file_name/mod.rs -expression: sorted_unreferenced_files - ---- -[] diff --git a/src/model/unreferenced_files/tests/file_stem/snapshots/unreferenced_files__model__unreferenced_files__tests__file_stem__found.snap b/src/model/unreferenced_files/tests/file_stem/snapshots/unreferenced_files__model__unreferenced_files__tests__file_stem__found.snap deleted file mode 100644 index 0ed9fbd..0000000 --- a/src/model/unreferenced_files/tests/file_stem/snapshots/unreferenced_files__model__unreferenced_files__tests__file_stem__found.snap +++ /dev/null @@ -1,6 +0,0 @@ ---- -source: src/model/unreferenced_files/tests/file_stem/mod.rs -expression: sorted_unreferenced_files - ---- -[] diff --git a/src/model/unreferenced_files/tests/multiple_searching/snapshots/unreferenced_files__model__unreferenced_files__tests__multiple_searching__found.snap b/src/model/unreferenced_files/tests/multiple_searching/snapshots/unreferenced_files__model__unreferenced_files__tests__multiple_searching__found.snap deleted file mode 100644 index c997668..0000000 --- a/src/model/unreferenced_files/tests/multiple_searching/snapshots/unreferenced_files__model__unreferenced_files__tests__multiple_searching__found.snap +++ /dev/null @@ -1,6 +0,0 @@ ---- -source: src/model/unreferenced_files/tests/multiple_searching/mod.rs -expression: sorted_unreferenced_files - ---- -[] diff --git a/src/model/unreferenced_files/tests/relative_path/snapshots/unreferenced_files__model__unreferenced_files__tests__relative_path__found.snap b/src/model/unreferenced_files/tests/relative_path/snapshots/unreferenced_files__model__unreferenced_files__tests__relative_path__found.snap deleted file mode 100644 index 8ea960d..0000000 --- a/src/model/unreferenced_files/tests/relative_path/snapshots/unreferenced_files__model__unreferenced_files__tests__relative_path__found.snap +++ /dev/null @@ -1,6 +0,0 @@ ---- -source: src/model/unreferenced_files/tests/relative_path/mod.rs -expression: sorted_unreferenced_files - ---- -[] diff --git a/src/model/unreferenced_files/tests/singular_searching/snapshots/unreferenced_files__model__unreferenced_files__tests__singular_searching__found.snap b/src/model/unreferenced_files/tests/singular_searching/snapshots/unreferenced_files__model__unreferenced_files__tests__singular_searching__found.snap deleted file mode 100644 index ea77625..0000000 --- a/src/model/unreferenced_files/tests/singular_searching/snapshots/unreferenced_files__model__unreferenced_files__tests__singular_searching__found.snap +++ /dev/null @@ -1,6 +0,0 @@ ---- -source: src/model/unreferenced_files/tests/singular_searching/mod.rs -expression: sorted_unreferenced_files - ---- -[] diff --git a/src/model/unreferenced_files/tests/singular_searching/snapshots/unreferenced_files__model__unreferenced_files__tests__singular_searching__not_found.snap b/src/model/unreferenced_files/tests/singular_searching/snapshots/unreferenced_files__model__unreferenced_files__tests__singular_searching__not_found.snap deleted file mode 100644 index 818b692..0000000 --- a/src/model/unreferenced_files/tests/singular_searching/snapshots/unreferenced_files__model__unreferenced_files__tests__singular_searching__not_found.snap +++ /dev/null @@ -1,13 +0,0 @@ ---- -source: src/model/unreferenced_files/tests/singular_searching/mod.rs -expression: sorted_unreferenced_files - ---- -[ - FilePathVariants { - file_canonicalize_path: "/tmp/folder/file1.txt", - file_relative_path: "./folder/file1.txt", - file_name: "file1.txt", - file_stem: "file1", - }, -] diff --git a/src/regex_utilities/mod.rs b/src/regex_utilities/mod.rs deleted file mode 100644 index c5d2fef..0000000 --- a/src/regex_utilities/mod.rs +++ /dev/null @@ -1,13 +0,0 @@ -use std::process::exit; - -use regex::Regex; - -pub fn get_regex(regex_string: &str) -> Regex { - match Regex::new(regex_string) { - Ok(regex) => regex, - Err(error) => { - error!("{:?}", error); - exit(crate::ERROR_EXIT_CODE); - } - } -} diff --git a/src/reporter/mod.rs b/src/reporter/mod.rs index d2dae81..6ad74ae 100644 --- a/src/reporter/mod.rs +++ b/src/reporter/mod.rs @@ -1,16 +1,12 @@ -use crate::model::file_path_variants::FilePathVariants; -use crate::model::unreferenced_files::UnreferencedFiles; +use std::collections::HashSet; -pub fn print(unreferenced_files: UnreferencedFiles, print_full_path: bool) { - let mut sorted_unreferenced_files: Vec<&FilePathVariants> = - unreferenced_files.unreferenced_files.iter().collect(); +use crate::file_path_variants::FilePathVariants; + +pub(crate) fn print(unreferenced_files: HashSet, print_full_path: bool) { + let mut sorted_unreferenced_files: Vec<&FilePathVariants> = unreferenced_files.iter().collect(); sorted_unreferenced_files.sort(); for unreferenced_file in sorted_unreferenced_files { - if print_full_path { - println!("{}", unreferenced_file.file_canonicalize_path); - } else { - println!("{}", unreferenced_file.file_relative_path); - } + unreferenced_file.print(print_full_path); } } diff --git a/src/search/mod.rs b/src/search/mod.rs new file mode 100644 index 0000000..9c8a6e6 --- /dev/null +++ b/src/search/mod.rs @@ -0,0 +1,77 @@ +use std::collections::HashSet; +use std::path::{Path, PathBuf}; + +use anyhow::{bail, Result}; + +use crate::filters::Filters; +use crate::search::raw_file::RawFile; + +#[cfg(not(test))] +mod raw_file; + +#[cfg(test)] +pub(crate) mod raw_file; + +pub struct Search { + pub(crate) raw_files: HashSet, +} + +impl Search { + pub fn new>(paths: &[T], filters: Filters) -> Result { + fn get_raw_files_in_directory(path: &Path, filters: &Filters) -> Result> { + let mut raw_files = HashSet::new(); + debug!( + "Searching the directory {:?} for files to search.", + path.display() + ); + + let entries = std::fs::read_dir(path)?; + + for dir_entry in entries { + let dir_entry = dir_entry?; + let path = dir_entry.path(); + + if path.is_file() { + if let Ok(raw_file) = get_raw_file(path, filters) { + raw_files.insert(raw_file); + } + } else { + raw_files.extend(get_raw_files_in_directory(path.as_path(), filters)?); + } + } + Ok(raw_files) + } + + fn get_raw_file(path: PathBuf, filters: &Filters) -> Result { + if path.is_file() { + let raw_file = RawFile::new(path)?; + + if filters.should_ignore(&raw_file.file_path_variants.file_canonicalize_path) { + debug!("Ignoring the file {raw_file:?} and not searching it."); + } else { + debug!("Adding {raw_file:?} to the files searching."); + return Ok(raw_file); + } + } + + bail!("Unable to get raw file."); + } + + let mut raw_files = HashSet::new(); + let pathbufs = crate::utilities::to_pathbufs(paths)?; + + for pathbuf in pathbufs { + if pathbuf.is_dir() { + raw_files.extend(get_raw_files_in_directory(&pathbuf, &filters)?); + } else if let Ok(raw_file) = get_raw_file(pathbuf, &filters) { + raw_files.insert(raw_file); + } + } + + info!( + "Loaded {} files to search within for references.", + raw_files.len() + ); + Ok(Search { raw_files }) + } +} diff --git a/src/search/raw_file/mod.rs b/src/search/raw_file/mod.rs new file mode 100644 index 0000000..6b4a923 --- /dev/null +++ b/src/search/raw_file/mod.rs @@ -0,0 +1,46 @@ +use std::path::PathBuf; + +use anyhow::{Context, Result}; +use regex::Regex; + +use crate::file_path_variants::FilePathVariants; + +#[cfg(not(test))] +#[derive(Hash, Clone, PartialOrd, PartialEq, Ord, Eq)] +pub(crate) struct RawFile { + pub(crate) file_path_variants: FilePathVariants, + file_content: String, +} + +// For unit testing. +#[cfg(test)] +#[derive(Hash, Clone, PartialOrd, PartialEq, Ord, Eq)] +pub(crate) struct RawFile { + pub(crate) file_path_variants: FilePathVariants, + pub(crate) file_content: String, +} + +impl RawFile { + pub(crate) fn new(path: PathBuf) -> Result { + let file_content = std::fs::read_to_string(&path).context(format!( + "Unable to read the file {:?}'s content.", + path.display() + ))?; + let file_path_variants = crate::file_path_variants::FilePathVariants::new(path)?; + + Ok(RawFile { + file_path_variants, + file_content, + }) + } + + pub(crate) fn is_match(&self, regex: &Regex) -> bool { + regex.is_match(&self.file_content) + } +} + +impl std::fmt::Debug for RawFile { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(&self.file_path_variants.file_relative_path) + } +} diff --git a/src/search_for/mod.rs b/src/search_for/mod.rs new file mode 100644 index 0000000..c91e57e --- /dev/null +++ b/src/search_for/mod.rs @@ -0,0 +1,150 @@ +use std::collections::HashSet; +use std::path::{Path, PathBuf}; + +use anyhow::Result; + +use crate::file_path_variants::FilePathVariants; +use crate::file_path_variants_regexes::FilePathVariantsRegexes; +use crate::filters::Filters; +use crate::search::Search; + +#[cfg(not(test))] +pub struct SearchFor { + search_for: HashSet, +} + +// For unit testing. +#[cfg(test)] +#[derive(Clone)] +pub struct SearchFor { + pub(crate) search_for: HashSet, +} + +impl SearchFor { + pub fn new>(paths: &[T], filters: Filters) -> Result { + fn get_file_path_variants_in_directory( + path: &Path, + filters: &Filters, + ) -> Result> { + let mut files_path_variants = HashSet::new(); + debug!( + "Searching the directory {:?} for files to search for.", + path.display() + ); + + let entries = std::fs::read_dir(path)?; + for dir_entry in entries { + let dir_entry = dir_entry?; + let path = dir_entry.path(); + + if path.is_file() { + if let Some(file_path_variants) = get_file_path_variants(path, filters) { + files_path_variants.insert(file_path_variants); + } + } else { + files_path_variants.extend(get_file_path_variants_in_directory( + path.as_path(), + filters, + )?); + } + } + + Ok(files_path_variants) + } + + fn get_file_path_variants(path: PathBuf, filters: &Filters) -> Option { + let file_path_variants = FilePathVariants::new(path).unwrap(); + + if filters.should_ignore(&file_path_variants.file_canonicalize_path) { + debug!( + "Ignoring the file {:?} and not searching for it.", + file_path_variants.file_relative_path + ); + } else { + debug!( + "Adding {:?} to the files searching for.", + file_path_variants.file_relative_path + ); + return Some(file_path_variants); + } + + None + } + + let mut search_for = HashSet::new(); + + let pathbufs = crate::utilities::to_pathbufs(paths)?; + for pathbuf in pathbufs { + if pathbuf.is_file() { + if let Some(file_path_variants) = get_file_path_variants(pathbuf, &filters) { + search_for.insert(file_path_variants); + } + } else { + search_for.extend(get_file_path_variants_in_directory(&pathbuf, &filters)?); + } + } + + info!("Found {} files to search for references.", search_for.len()); + Ok(SearchFor { search_for }) + } + + pub fn get_unreferenced_files( + &self, + searching: Search, + search_for_relative_path: bool, + search_for_file_name: bool, + search_for_file_stem: bool, + ) -> HashSet { + let file_path_variants_regexes = FilePathVariantsRegexes::new( + &self.search_for, + search_for_relative_path, + search_for_file_name, + search_for_file_stem, + ) + .unwrap(); + + let mut unreferenced_files = self.search_for.clone(); + + for search in searching.raw_files { + if !unreferenced_files.is_empty() { + debug!( + "Searching the file {:?}.", + search.file_path_variants.file_relative_path + ); + + unreferenced_files.retain(|unreferenced_file| { + if unreferenced_file != &search.file_path_variants { + if search_for_relative_path + && search.is_match( + file_path_variants_regexes + .get(&unreferenced_file.file_relative_path), + ) + { + return false; + } + + if search_for_file_name + && search.is_match( + file_path_variants_regexes.get(&unreferenced_file.file_name), + ) + { + return false; + } + + if search_for_file_stem + && search.is_match( + file_path_variants_regexes.get(&unreferenced_file.file_stem), + ) + { + return false; + } + } + + true + }); + } + } + + unreferenced_files + } +} diff --git a/src/model/unreferenced_files/tests/file_name/mod.rs b/src/tests/file_name/mod.rs similarity index 68% rename from src/model/unreferenced_files/tests/file_name/mod.rs rename to src/tests/file_name/mod.rs index ce78a43..750c322 100644 --- a/src/model/unreferenced_files/tests/file_name/mod.rs +++ b/src/tests/file_name/mod.rs @@ -7,13 +7,13 @@ const SEARCH_FOR_FILE_STEM: bool = false; #[test] fn test_file_name_searching_for_relative_path_found() { // Given - let mut unreferenced_files = UNREFERENCED_FILE1.clone(); + let search_for = UNREFERENCED_FILE1.clone(); let searching = raw_file_with_content!( "@test\npublic void testImporting() {\n import(\"./folder/file1.txt\");\n}" ); // When - unreferenced_files.remove_referenced_files( + let unreferenced_files = search_for.get_unreferenced_files( searching, SEARCH_FOR_RELATIVE_PATH, SEARCH_FOR_FILE_NAME, @@ -21,18 +21,18 @@ fn test_file_name_searching_for_relative_path_found() { ); // Then - assert_sorted_unreferenced_files_snapshot!(FOUND, unreferenced_files); + assert_unreferenced_files_snapshot!(FOUND, unreferenced_files); } #[test] fn test_file_name_searching_for_file_name_found() { // Given - let mut unreferenced_files = UNREFERENCED_FILE1.clone(); + let search_for = UNREFERENCED_FILE1.clone(); let searching = raw_file_with_content!("@test\npublic void testImporting() {\n import(\"file1.txt\");\n}"); // When - unreferenced_files.remove_referenced_files( + let unreferenced_files = search_for.get_unreferenced_files( searching, SEARCH_FOR_RELATIVE_PATH, SEARCH_FOR_FILE_NAME, @@ -40,18 +40,18 @@ fn test_file_name_searching_for_file_name_found() { ); // Then - assert_sorted_unreferenced_files_snapshot!(FOUND, unreferenced_files); + assert_unreferenced_files_snapshot!(FOUND, unreferenced_files); } #[test] fn test_file_name_searching_for_not_found() { // Given - let mut unreferenced_files = UNREFERENCED_FILE1.clone(); + let search_for = UNREFERENCED_FILE1.clone(); let searching = raw_file_with_content!("@test\npublic void testImporting() {\n import(\"file1\");\n}"); // When - unreferenced_files.remove_referenced_files( + let unreferenced_files = search_for.get_unreferenced_files( searching, SEARCH_FOR_RELATIVE_PATH, SEARCH_FOR_FILE_NAME, @@ -59,5 +59,5 @@ fn test_file_name_searching_for_not_found() { ); // Then - assert_sorted_unreferenced_files_snapshot!(NOT_FOUND, unreferenced_files); + assert_unreferenced_files_snapshot!(NOT_FOUND, unreferenced_files); } diff --git a/src/tests/file_name/snapshots/unreferenced_files__tests__file_name__found.snap b/src/tests/file_name/snapshots/unreferenced_files__tests__file_name__found.snap new file mode 100644 index 0000000..834ab16 --- /dev/null +++ b/src/tests/file_name/snapshots/unreferenced_files__tests__file_name__found.snap @@ -0,0 +1,5 @@ +--- +source: src/tests/file_name/mod.rs +expression: sorted_search_for +--- +[] diff --git a/src/model/unreferenced_files/tests/file_name/snapshots/unreferenced_files__model__unreferenced_files__tests__file_name__not_found.snap b/src/tests/file_name/snapshots/unreferenced_files__tests__file_name__not_found.snap similarity index 67% rename from src/model/unreferenced_files/tests/file_name/snapshots/unreferenced_files__model__unreferenced_files__tests__file_name__not_found.snap rename to src/tests/file_name/snapshots/unreferenced_files__tests__file_name__not_found.snap index c551a32..6d6a6b6 100644 --- a/src/model/unreferenced_files/tests/file_name/snapshots/unreferenced_files__model__unreferenced_files__tests__file_name__not_found.snap +++ b/src/tests/file_name/snapshots/unreferenced_files__tests__file_name__not_found.snap @@ -1,7 +1,6 @@ --- -source: src/model/unreferenced_files/tests/file_name/mod.rs -expression: sorted_unreferenced_files - +source: src/tests/file_name/mod.rs +expression: sorted_search_for --- [ FilePathVariants { diff --git a/src/model/unreferenced_files/tests/file_stem/mod.rs b/src/tests/file_stem/mod.rs similarity index 67% rename from src/model/unreferenced_files/tests/file_stem/mod.rs rename to src/tests/file_stem/mod.rs index 1600f53..2a4e1d0 100644 --- a/src/model/unreferenced_files/tests/file_stem/mod.rs +++ b/src/tests/file_stem/mod.rs @@ -7,13 +7,13 @@ const SEARCH_FOR_FILE_STEM: bool = true; #[test] fn test_file_stem_searching_for_relative_path_found() { // Given - let mut unreferenced_files = UNREFERENCED_FILE1.clone(); + let search_for = UNREFERENCED_FILE1.clone(); let searching = raw_file_with_content!( "@test\npublic void testImporting() {\n import(\"./folder/file1.txt\");\n}" ); // When - unreferenced_files.remove_referenced_files( + let unreferenced_files = search_for.get_unreferenced_files( searching, SEARCH_FOR_RELATIVE_PATH, SEARCH_FOR_FILE_NAME, @@ -21,18 +21,18 @@ fn test_file_stem_searching_for_relative_path_found() { ); // Then - assert_sorted_unreferenced_files_snapshot!(FOUND, unreferenced_files); + assert_unreferenced_files_snapshot!(FOUND, unreferenced_files); } #[test] fn test_file_stem_searching_for_file_name_found() { // Given - let mut unreferenced_files = UNREFERENCED_FILE1.clone(); + let search_for = UNREFERENCED_FILE1.clone(); let searching = raw_file_with_content!("@test\npublic void testImporting() {\n import(\"file1.txt\");\n}"); // When - unreferenced_files.remove_referenced_files( + let unreferenced_files = search_for.get_unreferenced_files( searching, SEARCH_FOR_RELATIVE_PATH, SEARCH_FOR_FILE_NAME, @@ -40,18 +40,18 @@ fn test_file_stem_searching_for_file_name_found() { ); // Then - assert_sorted_unreferenced_files_snapshot!(FOUND, unreferenced_files); + assert_unreferenced_files_snapshot!(FOUND, unreferenced_files); } #[test] fn test_file_stem_searching_for_file_stem_found() { // Given - let mut unreferenced_files = UNREFERENCED_FILE1.clone(); + let search_for = UNREFERENCED_FILE1.clone(); let searching = raw_file_with_content!("@test\npublic void testImporting() {\n import(\"file1\");\n}"); // When - unreferenced_files.remove_referenced_files( + let unreferenced_files = search_for.get_unreferenced_files( searching, SEARCH_FOR_RELATIVE_PATH, SEARCH_FOR_FILE_NAME, @@ -59,19 +59,19 @@ fn test_file_stem_searching_for_file_stem_found() { ); // Then - assert_sorted_unreferenced_files_snapshot!(FOUND, unreferenced_files); + assert_unreferenced_files_snapshot!(FOUND, unreferenced_files); } #[test] fn test_file_stem_searching_for_not_found() { // Given - let mut unreferenced_files = UNREFERENCED_FILE1.clone(); + let search_for = UNREFERENCED_FILE1.clone(); let searching = raw_file_with_content!( "@test\npublic void testImporting() {\n import(\"/tmp/folder/file2.txt\");\n}" ); // When - unreferenced_files.remove_referenced_files( + let unreferenced_files = search_for.get_unreferenced_files( searching, SEARCH_FOR_RELATIVE_PATH, SEARCH_FOR_FILE_NAME, @@ -79,5 +79,5 @@ fn test_file_stem_searching_for_not_found() { ); // Then - assert_sorted_unreferenced_files_snapshot!(NOT_FOUND, unreferenced_files); + assert_unreferenced_files_snapshot!(NOT_FOUND, unreferenced_files); } diff --git a/src/tests/file_stem/snapshots/unreferenced_files__tests__file_stem__found.snap b/src/tests/file_stem/snapshots/unreferenced_files__tests__file_stem__found.snap new file mode 100644 index 0000000..87a46ee --- /dev/null +++ b/src/tests/file_stem/snapshots/unreferenced_files__tests__file_stem__found.snap @@ -0,0 +1,5 @@ +--- +source: src/tests/file_stem/mod.rs +expression: sorted_search_for +--- +[] diff --git a/src/model/unreferenced_files/tests/file_stem/snapshots/unreferenced_files__model__unreferenced_files__tests__file_stem__not_found.snap b/src/tests/file_stem/snapshots/unreferenced_files__tests__file_stem__not_found.snap similarity index 67% rename from src/model/unreferenced_files/tests/file_stem/snapshots/unreferenced_files__model__unreferenced_files__tests__file_stem__not_found.snap rename to src/tests/file_stem/snapshots/unreferenced_files__tests__file_stem__not_found.snap index 17ac8f2..7c246b6 100644 --- a/src/model/unreferenced_files/tests/file_stem/snapshots/unreferenced_files__model__unreferenced_files__tests__file_stem__not_found.snap +++ b/src/tests/file_stem/snapshots/unreferenced_files__tests__file_stem__not_found.snap @@ -1,7 +1,6 @@ --- -source: src/model/unreferenced_files/tests/file_stem/mod.rs -expression: sorted_unreferenced_files - +source: src/tests/file_stem/mod.rs +expression: sorted_search_for --- [ FilePathVariants { diff --git a/src/model/unreferenced_files/tests/mod.rs b/src/tests/mod.rs similarity index 52% rename from src/model/unreferenced_files/tests/mod.rs rename to src/tests/mod.rs index 42626bb..3bfc511 100644 --- a/src/model/unreferenced_files/tests/mod.rs +++ b/src/tests/mod.rs @@ -1,19 +1,20 @@ -use crate::model::file_path_variants::FilePathVariants; -use crate::model::raw_file::RawFile; -use crate::model::raw_files::RawFiles; +use std::collections::HashSet; -use super::*; +use crate::file_path_variants::FilePathVariants; +use crate::search::raw_file::RawFile; +use crate::search::Search; +use crate::search_for::SearchFor; const FOUND: &str = "found"; const NOT_FOUND: &str = "not_found"; const MULTIPLE_NOT_FOUND: &str = "multiple_not_found"; lazy_static! { - static ref TEST_JAVA: FilePathVariants = FilePathVariants { - file_canonicalize_path: "/tmp/test.java".to_string(), - file_relative_path: "./test.java".to_string(), - file_name: "test.java".to_string(), - file_stem: "test".to_string(), + static ref SEARCH: FilePathVariants = FilePathVariants { + file_canonicalize_path: "/tmp/search.java".to_string(), + file_relative_path: "./search.java".to_string(), + file_name: "search.java".to_string(), + file_stem: "search".to_string(), }; static ref FILE1_TXT: FilePathVariants = FilePathVariants { file_canonicalize_path: "/tmp/folder/file1.txt".to_string(), @@ -27,26 +28,26 @@ lazy_static! { file_name: "file2.txt".to_string(), file_stem: "file2".to_string(), }; - static ref UNREFERENCED_FILE1: UnreferencedFiles = { - let mut unreferenced_files = HashSet::new(); - unreferenced_files.insert(FILE1_TXT.clone()); - UnreferencedFiles { unreferenced_files } + static ref UNREFERENCED_FILE1: SearchFor = { + let mut search_for = HashSet::new(); + search_for.insert(FILE1_TXT.clone()); + SearchFor { search_for } }; - static ref UNREFERENCED_FILE1_AND_FILE2: UnreferencedFiles = { - let mut unreferenced_files = HashSet::new(); - unreferenced_files.insert(FILE1_TXT.clone()); - unreferenced_files.insert(FILE2_TXT.clone()); - UnreferencedFiles { unreferenced_files } + static ref UNREFERENCED_FILE1_AND_FILE2: SearchFor = { + let mut search_for = HashSet::new(); + search_for.insert(FILE1_TXT.clone()); + search_for.insert(FILE2_TXT.clone()); + SearchFor { search_for } }; } macro_rules! raw_file_with_content { ($file_content:expr) => { - RawFiles { + Search { raw_files: { let mut raw_files = HashSet::new(); raw_files.insert(RawFile { - file_path_variants: TEST_JAVA.clone(), + file_path_variants: SEARCH.clone(), file_content: $file_content.to_string(), }); raw_files @@ -57,15 +58,15 @@ macro_rules! raw_file_with_content { macro_rules! raw_files_with_content { ($file1_content:expr, $file2_content:expr) => { - RawFiles { + Search { raw_files: { let mut raw_files = HashSet::new(); raw_files.insert(RawFile { - file_path_variants: TEST_JAVA.clone(), + file_path_variants: SEARCH.clone(), file_content: $file1_content.to_string(), }); raw_files.insert(RawFile { - file_path_variants: TEST_JAVA.clone(), + file_path_variants: SEARCH.clone(), file_content: $file2_content.to_string(), }); raw_files @@ -74,12 +75,11 @@ macro_rules! raw_files_with_content { }; } -macro_rules! assert_sorted_unreferenced_files_snapshot { - ($snapshot_name:expr, $unreferenced_files:expr) => { - let mut sorted_unreferenced_files: Vec<_> = - $unreferenced_files.unreferenced_files.iter().collect(); - sorted_unreferenced_files.sort(); - insta::assert_debug_snapshot!($snapshot_name, sorted_unreferenced_files); +macro_rules! assert_unreferenced_files_snapshot { + ($snapshot_name:expr, $search_for:expr) => { + let mut sorted_search_for: Vec<_> = $search_for.iter().collect(); + sorted_search_for.sort(); + insta::assert_debug_snapshot!($snapshot_name, sorted_search_for); }; } diff --git a/src/model/unreferenced_files/tests/multiple_searching/mod.rs b/src/tests/multiple_searching/mod.rs similarity index 70% rename from src/model/unreferenced_files/tests/multiple_searching/mod.rs rename to src/tests/multiple_searching/mod.rs index a6bef99..bd41f8c 100644 --- a/src/model/unreferenced_files/tests/multiple_searching/mod.rs +++ b/src/tests/multiple_searching/mod.rs @@ -7,14 +7,14 @@ const SEARCH_FOR_FILE_STEM: bool = true; #[test] fn test_singular_searching_for_found() { // Given - let mut unreferenced_files = UNREFERENCED_FILE1.clone(); + let search_for = UNREFERENCED_FILE1.clone(); let searching = raw_files_with_content!( "@test\npublic void testImporting() {\n import(\"./file1.txt\");\n}", "@test\npublic void testImporting() {\n import(\"./file2.txt\");\n}" ); // When - unreferenced_files.remove_referenced_files( + let unreferenced_files = search_for.get_unreferenced_files( searching, SEARCH_FOR_RELATIVE_PATH, SEARCH_FOR_FILE_NAME, @@ -22,20 +22,20 @@ fn test_singular_searching_for_found() { ); // Then - assert_sorted_unreferenced_files_snapshot!(FOUND, unreferenced_files); + assert_unreferenced_files_snapshot!(FOUND, unreferenced_files); } #[test] fn test_singular_searching_for_not_found() { // Given - let mut unreferenced_files = UNREFERENCED_FILE1.clone(); + let search_for = UNREFERENCED_FILE1.clone(); let searching = raw_files_with_content!( "@test\npublic void testImporting() {\n import(\"./file2.txt\");\n}", "@test\npublic void testImporting() {\n import(\"./file3.txt\");\n}" ); // When - unreferenced_files.remove_referenced_files( + let unreferenced_files = search_for.get_unreferenced_files( searching, SEARCH_FOR_RELATIVE_PATH, SEARCH_FOR_FILE_NAME, @@ -43,20 +43,20 @@ fn test_singular_searching_for_not_found() { ); // Then - assert_sorted_unreferenced_files_snapshot!(NOT_FOUND, unreferenced_files); + assert_unreferenced_files_snapshot!(NOT_FOUND, unreferenced_files); } #[test] fn test_multiple_searching_for_found() { // Given - let mut unreferenced_files = UNREFERENCED_FILE1_AND_FILE2.clone(); + let search_for = UNREFERENCED_FILE1_AND_FILE2.clone(); let searching = raw_files_with_content!( "@test\npublic void testImporting() {\n import(\"./file1.txt\");\n}", "@test\npublic void testImporting() {\n import(\"./file2.txt\");\n}" ); // When - unreferenced_files.remove_referenced_files( + let unreferenced_files = search_for.get_unreferenced_files( searching, SEARCH_FOR_RELATIVE_PATH, SEARCH_FOR_FILE_NAME, @@ -64,20 +64,20 @@ fn test_multiple_searching_for_found() { ); // Then - assert_sorted_unreferenced_files_snapshot!(FOUND, unreferenced_files); + assert_unreferenced_files_snapshot!(FOUND, unreferenced_files); } #[test] fn test_multiple_searching_for_not_found() { // Given - let mut unreferenced_files = UNREFERENCED_FILE1_AND_FILE2.clone(); + let search_for = UNREFERENCED_FILE1_AND_FILE2.clone(); let searching = raw_files_with_content!( "@test\npublic void testImporting() {\n import(\"./file3.txt\");\n}", "@test\npublic void testImporting() {\n import(\"./file4.txt\");\n}" ); // When - unreferenced_files.remove_referenced_files( + let unreferenced_files = search_for.get_unreferenced_files( searching, SEARCH_FOR_RELATIVE_PATH, SEARCH_FOR_FILE_NAME, @@ -85,5 +85,5 @@ fn test_multiple_searching_for_not_found() { ); // Then - assert_sorted_unreferenced_files_snapshot!(MULTIPLE_NOT_FOUND, unreferenced_files); + assert_unreferenced_files_snapshot!(MULTIPLE_NOT_FOUND, unreferenced_files); } diff --git a/src/tests/multiple_searching/snapshots/unreferenced_files__tests__multiple_searching__found.snap b/src/tests/multiple_searching/snapshots/unreferenced_files__tests__multiple_searching__found.snap new file mode 100644 index 0000000..b76e402 --- /dev/null +++ b/src/tests/multiple_searching/snapshots/unreferenced_files__tests__multiple_searching__found.snap @@ -0,0 +1,5 @@ +--- +source: src/tests/multiple_searching/mod.rs +expression: sorted_search_for +--- +[] diff --git a/src/model/unreferenced_files/tests/singular_searching/snapshots/unreferenced_files__model__unreferenced_files__tests__singular_searching__multiple_not_found.snap b/src/tests/multiple_searching/snapshots/unreferenced_files__tests__multiple_searching__multiple_not_found.snap similarity index 78% rename from src/model/unreferenced_files/tests/singular_searching/snapshots/unreferenced_files__model__unreferenced_files__tests__singular_searching__multiple_not_found.snap rename to src/tests/multiple_searching/snapshots/unreferenced_files__tests__multiple_searching__multiple_not_found.snap index c5b640f..a8ae5b6 100644 --- a/src/model/unreferenced_files/tests/singular_searching/snapshots/unreferenced_files__model__unreferenced_files__tests__singular_searching__multiple_not_found.snap +++ b/src/tests/multiple_searching/snapshots/unreferenced_files__tests__multiple_searching__multiple_not_found.snap @@ -1,7 +1,6 @@ --- -source: src/model/unreferenced_files/tests/singular_searching/mod.rs -expression: sorted_unreferenced_files - +source: src/tests/multiple_searching/mod.rs +expression: sorted_search_for --- [ FilePathVariants { diff --git a/src/model/unreferenced_files/tests/relative_path/snapshots/unreferenced_files__model__unreferenced_files__tests__relative_path__not_found.snap b/src/tests/multiple_searching/snapshots/unreferenced_files__tests__multiple_searching__not_found.snap similarity index 66% rename from src/model/unreferenced_files/tests/relative_path/snapshots/unreferenced_files__model__unreferenced_files__tests__relative_path__not_found.snap rename to src/tests/multiple_searching/snapshots/unreferenced_files__tests__multiple_searching__not_found.snap index fa5fca2..93a4b62 100644 --- a/src/model/unreferenced_files/tests/relative_path/snapshots/unreferenced_files__model__unreferenced_files__tests__relative_path__not_found.snap +++ b/src/tests/multiple_searching/snapshots/unreferenced_files__tests__multiple_searching__not_found.snap @@ -1,7 +1,6 @@ --- -source: src/model/unreferenced_files/tests/relative_path/mod.rs -expression: sorted_unreferenced_files - +source: src/tests/multiple_searching/mod.rs +expression: sorted_search_for --- [ FilePathVariants { diff --git a/src/model/unreferenced_files/tests/relative_path/mod.rs b/src/tests/relative_path/mod.rs similarity index 69% rename from src/model/unreferenced_files/tests/relative_path/mod.rs rename to src/tests/relative_path/mod.rs index 96263f3..a81ad98 100644 --- a/src/model/unreferenced_files/tests/relative_path/mod.rs +++ b/src/tests/relative_path/mod.rs @@ -7,13 +7,13 @@ const SEARCH_FOR_FILE_STEM: bool = false; #[test] fn test_relative_path_searching_for_found() { // Given - let mut unreferenced_files = UNREFERENCED_FILE1.clone(); + let search_for = UNREFERENCED_FILE1.clone(); let searching = raw_file_with_content!( "@test\npublic void testImporting() {\n import(\"./folder/file1.txt\");\n}" ); // When - unreferenced_files.remove_referenced_files( + let unreferenced_files = search_for.get_unreferenced_files( searching, SEARCH_FOR_RELATIVE_PATH, SEARCH_FOR_FILE_NAME, @@ -21,18 +21,18 @@ fn test_relative_path_searching_for_found() { ); // Then - assert_sorted_unreferenced_files_snapshot!(FOUND, unreferenced_files); + assert_unreferenced_files_snapshot!(FOUND, unreferenced_files); } #[test] fn test_relative_path_searching_for_not_found() { // Given - let mut unreferenced_files = UNREFERENCED_FILE1.clone(); + let search_for = UNREFERENCED_FILE1.clone(); let searching = raw_file_with_content!("@test\npublic void testImporting() {\n import(\"file1.txt\");\n}"); // When - unreferenced_files.remove_referenced_files( + let unreferenced_files = search_for.get_unreferenced_files( searching, SEARCH_FOR_RELATIVE_PATH, SEARCH_FOR_FILE_NAME, @@ -40,5 +40,5 @@ fn test_relative_path_searching_for_not_found() { ); // Then - assert_sorted_unreferenced_files_snapshot!(NOT_FOUND, unreferenced_files); + assert_unreferenced_files_snapshot!(NOT_FOUND, unreferenced_files); } diff --git a/src/tests/relative_path/snapshots/unreferenced_files__tests__relative_path__found.snap b/src/tests/relative_path/snapshots/unreferenced_files__tests__relative_path__found.snap new file mode 100644 index 0000000..0d7cafa --- /dev/null +++ b/src/tests/relative_path/snapshots/unreferenced_files__tests__relative_path__found.snap @@ -0,0 +1,5 @@ +--- +source: src/tests/relative_path/mod.rs +expression: sorted_search_for +--- +[] diff --git a/src/model/unreferenced_files/tests/multiple_searching/snapshots/unreferenced_files__model__unreferenced_files__tests__multiple_searching__not_found.snap b/src/tests/relative_path/snapshots/unreferenced_files__tests__relative_path__not_found.snap similarity index 65% rename from src/model/unreferenced_files/tests/multiple_searching/snapshots/unreferenced_files__model__unreferenced_files__tests__multiple_searching__not_found.snap rename to src/tests/relative_path/snapshots/unreferenced_files__tests__relative_path__not_found.snap index f72b92f..ca2c9dd 100644 --- a/src/model/unreferenced_files/tests/multiple_searching/snapshots/unreferenced_files__model__unreferenced_files__tests__multiple_searching__not_found.snap +++ b/src/tests/relative_path/snapshots/unreferenced_files__tests__relative_path__not_found.snap @@ -1,7 +1,6 @@ --- -source: src/model/unreferenced_files/tests/multiple_searching/mod.rs -expression: sorted_unreferenced_files - +source: src/tests/relative_path/mod.rs +expression: sorted_search_for --- [ FilePathVariants { diff --git a/src/model/unreferenced_files/tests/singular_searching/mod.rs b/src/tests/singular_searching/mod.rs similarity index 66% rename from src/model/unreferenced_files/tests/singular_searching/mod.rs rename to src/tests/singular_searching/mod.rs index 69e52da..4bcc995 100644 --- a/src/model/unreferenced_files/tests/singular_searching/mod.rs +++ b/src/tests/singular_searching/mod.rs @@ -7,13 +7,13 @@ const SEARCH_FOR_FILE_STEM: bool = true; #[test] fn test_singular_searching_for_found() { // Given - let mut unreferenced_files = UNREFERENCED_FILE1.clone(); + let search_for = UNREFERENCED_FILE1.clone(); let searching = raw_file_with_content!( "@test\npublic void testImporting() {\n import(\"./file1.txt\");\n}" ); // When - unreferenced_files.remove_referenced_files( + let unreferenced_files = search_for.get_unreferenced_files( searching, SEARCH_FOR_RELATIVE_PATH, SEARCH_FOR_FILE_NAME, @@ -21,19 +21,19 @@ fn test_singular_searching_for_found() { ); // Then - assert_sorted_unreferenced_files_snapshot!(FOUND, unreferenced_files); + assert_unreferenced_files_snapshot!(FOUND, unreferenced_files); } #[test] fn test_singular_searching_for_not_found() { // Given - let mut unreferenced_files = UNREFERENCED_FILE1.clone(); + let search_for = UNREFERENCED_FILE1.clone(); let searching = raw_file_with_content!( "@test\npublic void testImporting() {\n import(\"./file2.txt\");\n}" ); // When - unreferenced_files.remove_referenced_files( + let unreferenced_files = search_for.get_unreferenced_files( searching, SEARCH_FOR_RELATIVE_PATH, SEARCH_FOR_FILE_NAME, @@ -41,19 +41,19 @@ fn test_singular_searching_for_not_found() { ); // Then - assert_sorted_unreferenced_files_snapshot!(NOT_FOUND, unreferenced_files); + assert_unreferenced_files_snapshot!(NOT_FOUND, unreferenced_files); } #[test] fn test_multiple_searching_for_found() { // Given - let mut unreferenced_files = UNREFERENCED_FILE1_AND_FILE2.clone(); + let search_for = UNREFERENCED_FILE1_AND_FILE2.clone(); let searching = raw_file_with_content!( "@test\npublic void testImporting() {\n import(\"./file2.txt\");\n import(\"./file1.txt\");\n}" ); // When - unreferenced_files.remove_referenced_files( + let unreferenced_files = search_for.get_unreferenced_files( searching, SEARCH_FOR_RELATIVE_PATH, SEARCH_FOR_FILE_NAME, @@ -61,20 +61,20 @@ fn test_multiple_searching_for_found() { ); // Then - assert_sorted_unreferenced_files_snapshot!(FOUND, unreferenced_files); + assert_unreferenced_files_snapshot!(FOUND, unreferenced_files); } #[test] fn test_multiple_searching_for_not_found() { // Given - let mut unreferenced_files = UNREFERENCED_FILE1_AND_FILE2.clone(); + let search_for = UNREFERENCED_FILE1_AND_FILE2.clone(); let searching = raw_file_with_content!( "@test\npublic void testImporting() {\n import(\"./file3.txt\");\n import(\"./file4.txt\");\n}" ); // When - unreferenced_files.remove_referenced_files( + let unreferenced_files = search_for.get_unreferenced_files( searching, SEARCH_FOR_RELATIVE_PATH, SEARCH_FOR_FILE_NAME, @@ -82,5 +82,5 @@ fn test_multiple_searching_for_not_found() { ); // Then - assert_sorted_unreferenced_files_snapshot!(MULTIPLE_NOT_FOUND, unreferenced_files); + assert_unreferenced_files_snapshot!(MULTIPLE_NOT_FOUND, unreferenced_files); } diff --git a/src/tests/singular_searching/snapshots/unreferenced_files__tests__singular_searching__found.snap b/src/tests/singular_searching/snapshots/unreferenced_files__tests__singular_searching__found.snap new file mode 100644 index 0000000..9a647a8 --- /dev/null +++ b/src/tests/singular_searching/snapshots/unreferenced_files__tests__singular_searching__found.snap @@ -0,0 +1,5 @@ +--- +source: src/tests/singular_searching/mod.rs +expression: sorted_search_for +--- +[] diff --git a/src/model/unreferenced_files/tests/multiple_searching/snapshots/unreferenced_files__model__unreferenced_files__tests__multiple_searching__multiple_not_found.snap b/src/tests/singular_searching/snapshots/unreferenced_files__tests__singular_searching__multiple_not_found.snap similarity index 78% rename from src/model/unreferenced_files/tests/multiple_searching/snapshots/unreferenced_files__model__unreferenced_files__tests__multiple_searching__multiple_not_found.snap rename to src/tests/singular_searching/snapshots/unreferenced_files__tests__singular_searching__multiple_not_found.snap index 29cd9fd..b9e0da3 100644 --- a/src/model/unreferenced_files/tests/multiple_searching/snapshots/unreferenced_files__model__unreferenced_files__tests__multiple_searching__multiple_not_found.snap +++ b/src/tests/singular_searching/snapshots/unreferenced_files__tests__singular_searching__multiple_not_found.snap @@ -1,7 +1,6 @@ --- -source: src/model/unreferenced_files/tests/multiple_searching/mod.rs -expression: sorted_unreferenced_files - +source: src/tests/singular_searching/mod.rs +expression: sorted_search_for --- [ FilePathVariants { diff --git a/src/tests/singular_searching/snapshots/unreferenced_files__tests__singular_searching__not_found.snap b/src/tests/singular_searching/snapshots/unreferenced_files__tests__singular_searching__not_found.snap new file mode 100644 index 0000000..deaf52a --- /dev/null +++ b/src/tests/singular_searching/snapshots/unreferenced_files__tests__singular_searching__not_found.snap @@ -0,0 +1,12 @@ +--- +source: src/tests/singular_searching/mod.rs +expression: sorted_search_for +--- +[ + FilePathVariants { + file_canonicalize_path: "/tmp/folder/file1.txt", + file_relative_path: "./folder/file1.txt", + file_name: "file1.txt", + file_stem: "file1", + }, +] diff --git a/src/utilities/mod.rs b/src/utilities/mod.rs new file mode 100644 index 0000000..c2e7dae --- /dev/null +++ b/src/utilities/mod.rs @@ -0,0 +1,24 @@ +use std::path::{Path, PathBuf}; + +use anyhow::{bail, Result}; + +pub(crate) fn to_pathbufs>(paths: &[T]) -> Result> { + fn to_pathbuf>(path: T) -> Result { + let path = Path::new(path.as_ref()); + + if !path.exists() { + bail!(format!("{:?} does not exist.", path)); + } + + Ok(path.to_path_buf()) + } + + let mut pathbufs = vec![]; + + for path in paths { + let pathbuf = to_pathbuf(path)?; + pathbufs.push(pathbuf); + } + + Ok(pathbufs) +}