pax_global_header00006660000000000000000000000064151654655110014523gustar00rootroot0000000000000052 comment=8bea824a6c2d421df115b77db4388892bee2b084 anchore-go-collections-ebf4aeb/000077500000000000000000000000001516546551100167135ustar00rootroot00000000000000anchore-go-collections-ebf4aeb/.binny.yaml000066400000000000000000000006471516546551100210030ustar00rootroot00000000000000# only pull in version updates that were released more than a week ago (low-pass filter for quickly-retracted releases) cooldown: 7d # other binary versions inherited from .binny.yaml file in the anchore/go-make repo (version pinned in .make). # If you need to override a tool version, please do so by adding an entry here and go-make will # account for the local definition over the go-make shared definitions. tools: [] anchore-go-collections-ebf4aeb/.bouncer.yaml000066400000000000000000000003251516546551100213120ustar00rootroot00000000000000permit: - BSD.* - MIT.* - Apache.* - MPL.* - CC0-1.0 ignore-packages: # crypto/internal/boring is released under the openSSL license as a part of the Golang Standard Libary - crypto/internal/boring anchore-go-collections-ebf4aeb/.chronicle.yaml000066400000000000000000000002651516546551100216260ustar00rootroot00000000000000# no matter the change, stay v0 for now enforce-v0: true # since github release pages already have titles that are the tag, we don't need to repeat that in the changelog title: "" anchore-go-collections-ebf4aeb/.github/000077500000000000000000000000001516546551100202535ustar00rootroot00000000000000anchore-go-collections-ebf4aeb/.github/dependabot.yml000066400000000000000000000027551516546551100231140ustar00rootroot00000000000000# Dependabot configuration # # Grouping behavior (see inline comments for details): # - Minor + patch updates: grouped into a single PR per ecosystem # - Major version bumps: individual PR per dependency # - Security updates: individual PR per dependency # # Note: "patch" refers to semver version bumps (1.2.3 -> 1.2.4), not security fixes. # Security updates are identified separately via GitHub's Advisory Database and # can be any version bump (patch, minor, or major) that fixes a known CVE. version: 2 updates: - package-ecosystem: gomod directories: - "/" - "/.make" cooldown: default-days: 7 schedule: interval: "weekly" day: "friday" open-pull-requests-limit: 10 labels: - "dependencies" groups: go-minor-patch: applies-to: version-updates # security updates get individual PRs patterns: - "*" update-types: # major omitted, gets individual PRs - "minor" - "patch" - package-ecosystem: "github-actions" directories: - "/" - "/.github/actions/*" cooldown: default-days: 7 schedule: interval: "weekly" day: "friday" open-pull-requests-limit: 10 labels: - "dependencies" groups: actions-minor-patch: applies-to: version-updates # security updates get individual PRs patterns: - "*" update-types: # major omitted, gets individual PRs - "minor" - "patch" anchore-go-collections-ebf4aeb/.github/workflows/000077500000000000000000000000001516546551100223105ustar00rootroot00000000000000anchore-go-collections-ebf4aeb/.github/workflows/codeql.yaml000066400000000000000000000026211516546551100244440ustar00rootroot00000000000000name: "CodeQL" on: push: branches: [ "main" ] pull_request: branches: [ "main" ] schedule: - cron: '38 11 * * 3' jobs: analyze: name: Analyze (${{ matrix.language }}) runs-on: ubuntu-latest permissions: security-events: write packages: read actions: read contents: read strategy: fail-fast: false matrix: include: - language: actions build-mode: none - language: go build-mode: manual steps: - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: Setup Go if: matrix.language == 'go' uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 with: go-version-file: go.mod - name: Initialize CodeQL uses: github/codeql-action/init@c10b8064de6f491fea524254123dbe5e09572f13 # v4.35.1 with: languages: ${{ matrix.language }} build-mode: ${{ matrix.build-mode }} - name: Build (Go) if: matrix.build-mode == 'manual' shell: bash run: go build ./... - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@c10b8064de6f491fea524254123dbe5e09572f13 # v4.35.1 with: category: "/language:${{matrix.language}}" anchore-go-collections-ebf4aeb/.github/workflows/dependabot-automation.yaml000066400000000000000000000002611516546551100274560ustar00rootroot00000000000000name: Dependabot Automation on: pull_request: permissions: pull-requests: write jobs: run: uses: anchore/workflows/.github/workflows/dependabot-automation.yaml@main anchore-go-collections-ebf4aeb/.github/workflows/oss-project-board-add.yaml000066400000000000000000000005361516546551100272630ustar00rootroot00000000000000name: Add to OSS board on: issues: types: - opened - reopened - transferred - labeled permissions: issues: read # Needed to read issue data for project board jobs: run: uses: "anchore/workflows/.github/workflows/oss-project-board-add.yaml@main" secrets: token: ${{ secrets.OSS_PROJECT_GH_TOKEN }} anchore-go-collections-ebf4aeb/.github/workflows/release.yaml000066400000000000000000000036671516546551100246300ustar00rootroot00000000000000name: "Release" permissions: contents: read # there should never be two releases in progress at the same time concurrency: group: release cancel-in-progress: false on: workflow_dispatch: inputs: version: description: tag the latest commit on main with the given version (prefixed with v) required: true jobs: version-available: uses: anchore/workflows/.github/workflows/check-version-available.yaml@4f25313f96311410cad4173f74617654a3e46d48 # v0.3.0 with: version: ${{ github.event.inputs.version }} check-gate: permissions: checks: read # required for getting the status of specific check names uses: anchore/workflows/.github/workflows/check-gate.yaml@4f25313f96311410cad4173f74617654a3e46d48 # v0.3.0 with: # these are checks that should be run on pull-request and merges to main. # we do NOT want to kick off a release if these have not been verified on main. # Please see the validations.yaml workflow for the names that should be used here. checks: '["Static analysis", "Unit tests"]' release: needs: [check-gate, version-available] environment: release # contains secrets needed for release runs-on: ubuntu-24.04 permissions: contents: write # needed for creating github release objects steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd #v6.0.2 with: fetch-depth: 0 # we need the full history to reason about changelogs and tags persist-credentials: true # needed for pushing a tag # setup checkout, go, go-make, binny, and cache go modules - uses: anchore/go-make/.github/actions/setup@383ef7852b8ae43a30f424896b52479186d2ea4d # v0.1.0 - name: Create release env: GITHUB_TOKEN: ${{ github.token }} DEPLOY_KEY: ${{ secrets.DEPLOY_KEY }} RELEASE_VERSION: ${{ github.event.inputs.version }} run: make ci-release anchore-go-collections-ebf4aeb/.github/workflows/validate-github-actions.yaml000066400000000000000000000015011516546551100277000ustar00rootroot00000000000000name: "Validate GitHub Actions" on: workflow_dispatch: pull_request: push: branches: - main paths: - '.github/workflows/**' - '.github/actions/**' permissions: contents: read jobs: zizmor: name: "Lint" runs-on: ubuntu-latest permissions: contents: read security-events: write # for uploading SARIF results steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: "Run zizmor" uses: zizmorcore/zizmor-action@71321a20a9ded102f6e9ce5718a2fcec2c4f70d8 # v0.5.2 with: # there is a pass/fail gate as a repo ruleset (if there is no ruleset configured then the action will pass by default) advanced-security: true inputs: .github anchore-go-collections-ebf4aeb/.github/workflows/validations.yaml000066400000000000000000000017211516546551100255120ustar00rootroot00000000000000name: "Validations" on: workflow_dispatch: push: branches: - main pull_request: permissions: contents: read jobs: Static-Analysis: # Note: changing this job name requires making the same update in the .github/workflows/release.yaml pipeline name: "Static analysis" runs-on: ubuntu-24.04 steps: # setup checkout, go, go-make, binny, and cache go modules - uses: anchore/go-make/.github/actions/setup@383ef7852b8ae43a30f424896b52479186d2ea4d # v0.1.0 - name: Run static analysis run: make static-analysis Unit-Test: # Note: changing this job name requires making the same update in the .github/workflows/release.yaml pipeline name: "Unit tests" runs-on: ubuntu-24.04 steps: # setup checkout, go, go-make, binny, and cache go modules - uses: anchore/go-make/.github/actions/setup@383ef7852b8ae43a30f424896b52479186d2ea4d # v0.1.0 - name: Run unit tests run: make unit anchore-go-collections-ebf4aeb/.github/zizmor.yml000066400000000000000000000002751516546551100223340ustar00rootroot00000000000000rules: unpinned-uses: ignore: # Allow unpinned uses of trusted internal anchore/workflows actions - oss-project-board-add.yaml - remove-awaiting-response-label.yaml anchore-go-collections-ebf4aeb/.gitignore000066400000000000000000000010671516546551100207070ustar00rootroot00000000000000# local development go.work go.work.sum mise.toml /specs/ # IDEs .idea/ .vscode/ .history/ # tools and aux data .tool .tmp .task # release info /CHANGELOG.md /VERSION # archives and fixtures /test/results /dist /snapshot .server/ *.fingerprint *.tar *.jar *.war *.ear *.jpi *.hpi *.zip *.log .images .tmp/ coverage.txt bin/ # Binaries for programs and plugins *.exe *.exe~ *.dll *.so *.dylib # Test binary, build with `go test -c` *.test # Output of the go coverage tool, specifically when used with LiteIDE *.out # macOS Finder metadata .DS_STORE *.profile anchore-go-collections-ebf4aeb/.golangci.yaml000066400000000000000000000055711516546551100214500ustar00rootroot00000000000000version: "2" run: tests: false linters: default: none enable: - asciicheck - bodyclose - copyloopvar - dogsled - dupl - errcheck - funlen - gocognit - goconst - gocritic - gocyclo - goprintffuncname - gosec - govet - ineffassign - misspell - nakedret - nolintlint - revive - staticcheck - unconvert - unparam - unused - whitespace settings: funlen: lines: 100 statements: 60 gocognit: min-complexity: 40 gocritic: enabled-checks: - deferInLoop gosec: excludes: - G115 exclusions: generated: lax presets: - comments - common-false-positives - legacy - std-error-handling paths: - third_party$ - builtin$ - examples$ # do not enable... # - deadcode # The owner seems to have abandoned the linter. Replaced by "unused". # - depguard # We don't have a configuration for this yet # - goprintffuncname # does not catch all cases and there are exceptions # - nakedret # does not catch all cases and should not fail a build # - gochecknoglobals # - gochecknoinits # this is too aggressive # - rowserrcheck disabled per generics https://github.com/golangci/golangci-lint/issues/2649 # - godot # - godox # - goerr113 # - goimports # we're using gosimports now instead to account for extra whitespaces (see https://github.com/golang/go/issues/20818) # - golint # deprecated # - gomnd # this is too aggressive # - interfacer # this is a good idea, but is no longer supported and is prone to false positives # - lll # without a way to specify per-line exception cases, this is not usable # - maligned # this is an excellent linter, but tricky to optimize and we are not sensitive to memory layout optimizations # - nestif # - prealloc # following this rule isn't consistently a good idea, as it sometimes forces unnecessary allocations that result in less idiomatic code # - rowserrcheck # not in a repo with sql, so this is not useful # - scopelint # deprecated # - structcheck # The owner seems to have abandoned the linter. Replaced by "unused". # - testpackage # - varcheck # The owner seems to have abandoned the linter. Replaced by "unused". # - wsl # this doens't have an auto-fixer yet and is pretty noisy (https://github.com/bombsimon/wsl/issues/90) issues: max-same-issues: 25 uniq-by-line: false # TODO: enable this when we have coverage on docstring comments # # The list of ids of default excludes to include or disable. # include: # - EXC0002 # disable excluding of issues about comments from golint formatters: enable: - gofmt - goimports exclusions: generated: lax paths: - third_party$ - builtin$ - examples$ anchore-go-collections-ebf4aeb/.make/000077500000000000000000000000001516546551100177065ustar00rootroot00000000000000anchore-go-collections-ebf4aeb/.make/go.mod000066400000000000000000000004021516546551100210100ustar00rootroot00000000000000module builder go 1.26.2 require github.com/anchore/go-make v0.1.0 require ( github.com/bmatcuk/doublestar/v4 v4.10.0 // indirect github.com/goccy/go-yaml v1.19.2 // indirect golang.org/x/mod v0.34.0 // indirect golang.org/x/sys v0.42.0 // indirect ) anchore-go-collections-ebf4aeb/.make/go.sum000066400000000000000000000014771516546551100210520ustar00rootroot00000000000000github.com/anchore/go-make v0.1.0 h1:w/DTKznE1s0u5H1ahAnLaHRbCyNuOn5sJd0UltKWCIA= github.com/anchore/go-make v0.1.0/go.mod h1:JafD2Md95wih7aGmA12yVoReZW3mOgIuwHbw5aOcIOQ= github.com/bmatcuk/doublestar/v4 v4.10.0 h1:zU9WiOla1YA122oLM6i4EXvGW62DvKZVxIe6TYWexEs= github.com/bmatcuk/doublestar/v4 v4.10.0/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM= github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= golang.org/x/mod v0.34.0 h1:xIHgNUUnW6sYkcM5Jleh05DvLOtwc6RitGHbDk4akRI= golang.org/x/mod v0.34.0/go.mod h1:ykgH52iCZe79kzLLMhyCUzhMci+nQj+0XkbXpNYtVjY= golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo= golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= anchore-go-collections-ebf4aeb/.make/main.go000066400000000000000000000004741516546551100211660ustar00rootroot00000000000000package main import ( . "github.com/anchore/go-make" "github.com/anchore/go-make/tasks/golint" "github.com/anchore/go-make/tasks/gotest" "github.com/anchore/go-make/tasks/release" ) func main() { Makefile( gotest.Tasks(), golint.Tasks(), release.ChangelogTask(), release.TagAndCreateGHRelease(), ) } anchore-go-collections-ebf4aeb/CONTRIBUTING.md000066400000000000000000000072431516546551100211520ustar00rootroot00000000000000# Contributing to go-collections If you are looking to contribute to this project and want to open a GitHub pull request ("PR"), there are a few guidelines of what we are looking for in patches. Make sure you go through this document and ensure that your code proposal is aligned. ## Sign off your work The `sign-off` is an added line at the end of the explanation for the commit, certifying that you wrote it or otherwise have the right to submit it as an open-source patch. By submitting a contribution, you agree to be bound by the terms of the DCO Version 1.1 and Apache License Version 2.0. Signing off a commit certifies the below Developer's Certificate of Origin (DCO): ```text Developer's Certificate of Origin 1.1 By making a contribution to this project, I certify that: (a) The contribution was created in whole or in part by me and I have the right to submit it under the open source license indicated in the file; or (b) The contribution is based upon previous work that, to the best of my knowledge, is covered under an appropriate open source license and I have the right under that license to submit that work with modifications, whether created in whole or in part by me, under the same open source license (unless I am permitted to submit under a different license), as indicated in the file; or (c) The contribution was provided directly to me by some other person who certified (a), (b) or (c) and I have not modified it. (d) I understand and agree that this project and the contribution are public and that a record of the contribution (including all personal information I submit with it, including my sign-off) is maintained indefinitely and may be redistributed consistent with this project or the open source license(s) involved. ``` All contributions to this project are licensed under the [Apache License Version 2.0, January 2004](http://www.apache.org/licenses/). When committing your change, you can add the required line manually so that it looks like this: ```text Signed-off-by: John Doe ``` Alternatively, configure your Git client with your name and email to use the `-s` flag when creating a commit: ```text $ git config --global user.name "John Doe" $ git config --global user.email "john.doe@example.com" ``` Creating a signed-off commit is then possible with `-s` or `--signoff`: ```text $ git commit -s -m "this is a commit message" ``` To double-check that the commit was signed-off, look at the log output: ```text $ git log -1 commit 37ceh170e4hb283bb73d958f2036ee5k07e7fde7 (HEAD -> issue-35, origin/main, main) Author: John Doe Date: Mon Aug 1 11:27:13 2020 -0400 this is a commit message Signed-off-by: John Doe ``` [//]: # (TODO: Commit guidelines, granular commits) [//]: # (TODO: Commit guidelines, descriptive messages) [//]: # (TODO: Commit guidelines, commit title, extra body description) [//]: # (TODO: PR title and description) ## Test your changes This project has a `Makefile` which includes many helpers running both unit and integration tests. Although PRs will have automatic checks for these, it is useful to run them locally, ensuring they pass before submitting changes. Ensure you've bootstrapped once before running tests: ```text $ make bootstrap ``` You only need to bootstrap once. After the bootstrap process, you can run the tests as many times as needed: ```text $ make unit ``` ## Document your changes When proposed changes are modifying user-facing functionality or output, it is expected the PR will include updates to the documentation as well. anchore-go-collections-ebf4aeb/LICENSE000066400000000000000000000261351516546551100177270ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. anchore-go-collections-ebf4aeb/Makefile000066400000000000000000000000561516546551100203540ustar00rootroot00000000000000.PHONY: * .DEFAULT: %: @go run -C .make . $@ anchore-go-collections-ebf4aeb/README.md000066400000000000000000000016541516546551100202000ustar00rootroot00000000000000# go-collections [![Go Report Card](https://goreportcard.com/badge/github.com/anchore/go-collections)](https://goreportcard.com/report/github.com/anchore/go-collections) [![GitHub go.mod Go version](https://img.shields.io/github/go-mod/go-version/anchore/go-collections.svg)](https://github.com/anchore/go-collections) [![License: Apache-2.0](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://github.com/anchore/go-collections/blob/main/LICENSE) [![Slack Invite](https://img.shields.io/badge/Slack-Join-blue?logo=slack)](https://anchore.com/slack) A library with generic data structures for common collection operations. ## Status ***Consider this project to be in alpha. The API is not stable and may change at any time.*** ## Overview `collections.TaggedValue` a set of tags associated with a value `collections.TaggedValueSet` provides methods for performing set operations on an arbitrary set of tagged values anchore-go-collections-ebf4aeb/go.mod000066400000000000000000000003631516546551100200230ustar00rootroot00000000000000module github.com/anchore/go-collections go 1.21.0 require github.com/stretchr/testify v1.8.4 require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) anchore-go-collections-ebf4aeb/go.sum000066400000000000000000000015611516546551100200510ustar00rootroot00000000000000github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= anchore-go-collections-ebf4aeb/tagged_value.go000066400000000000000000000072751516546551100217040ustar00rootroot00000000000000package collections import ( "fmt" "reflect" "slices" ) // TaggedValue holds an arbitrary value with associated tags type TaggedValue[T any] struct { Value T Tags []string } // HasTag indicates the TaggedValue has a tag matching one or more of the provided arguments func (t TaggedValue[T]) HasTag(tags ...string) bool { for _, tag := range tags { for _, existing := range t.Tags { if tag == existing { return true } } } return false } // NewTaggedValue returns a tagged value, that can be added, for example, to a TaggedValueSet collection func NewTaggedValue[T any](value T, tags ...string) TaggedValue[T] { return TaggedValue[T]{ Value: value, Tags: tags, } } // TaggedValueSet is a utility to handle a sorted set of tagged items including basic filtering type TaggedValueSet[T comparable] []TaggedValue[T] // Values returns a slice containing the values in the set func (t TaggedValueSet[T]) Values() []T { out := make([]T, len(t)) for i, v := range t { out[i] = v.Value } return out } // HasTag indicates this set contains one or more items matching any of the provided tags func (t TaggedValueSet[T]) HasTag(tags ...string) bool { for _, tagged := range t { if tagged.HasTag(tags...) { return true } } return false } // HasValue indicates any of the provided values is present in this set func (t TaggedValueSet[T]) HasValue(value ...T) bool { for _, tagged := range t { for _, v := range value { if isEqual(tagged.Value, v) { return true } } } return false } // Select returns a new set of values matching any of the provided tags, ordered by the provided tags func (t TaggedValueSet[T]) Select(tags ...string) TaggedValueSet[T] { if len(tags) == 0 { return TaggedValueSet[T]{} } out := make(TaggedValueSet[T], 0, len(t)) for _, tag := range tags { for _, existing := range t { if existing.HasTag(tag) { if out.HasValue(existing.Value) { continue } out = append(out, existing) } } } return out } // Remove returns a new set of values, excluding those with any of the provided tags func (t TaggedValueSet[T]) Remove(tags ...string) TaggedValueSet[T] { if len(tags) == 0 { return t } out := make(TaggedValueSet[T], 0, len(t)) for _, tagged := range t { if !tagged.HasTag(tags...) { out = append(out, tagged) } } return out } // Join returns a new set of values, combining this set and the provided values, omitting duplicates func (t TaggedValueSet[T]) Join(taggedValues ...TaggedValue[T]) TaggedValueSet[T] { if len(taggedValues) == 0 { return t } out := make(TaggedValueSet[T], 0, len(t)+len(taggedValues)) out = append(out, t...) for _, value := range taggedValues { if t.HasValue(value.Value) { continue } out = append(out, value) } return out } // Tags returns the unique set of tags from all entries func (t TaggedValueSet[T]) Tags() (tags []string) { for _, entry := range t { for _, tag := range entry.Tags { if !slices.Contains(tags, tag) { tags = append(tags, tag) } } } return tags } // since T in TaggedValueSet[T] is comparable, we can use == but this can panic if T is an interface and specific values are // used which implement the interface but are not comparable. to avoid panics, we check if both values are comparable. // additionally, if we wanted to use function references directly e.g. tagged.New(&package.Func), this function implements // a workaround to properly compare these types func isEqual[T any](a, b T) bool { va := reflect.ValueOf(a) vb := reflect.ValueOf(b) if va.Type().Comparable() && vb.Type().Comparable() { return va.Equal(vb) } if va.Type().Kind() == reflect.Func { return fmt.Sprintf("%v", a) == fmt.Sprintf("%v", b) } return reflect.DeepEqual(a, b) } anchore-go-collections-ebf4aeb/tagged_value_test.go000066400000000000000000000126701516546551100227360ustar00rootroot00000000000000package collections import ( "testing" "github.com/stretchr/testify/require" ) func Test_TaggedInterfaces(t *testing.T) { type t1 struct { name string field []string // not comparable } a := []string{"a"} b := []string{"b"} c := []string{"c"} set1 := TaggedValueSet[any]{TaggedValue[any]{Value: t1{name: "a"}, Tags: a}, TaggedValue[any]{Value: t1{name: "b"}, Tags: b}} set2 := TaggedValueSet[any]{TaggedValue[any]{Value: t1{name: "c"}, Tags: c}} s := set2.Join(set2...) require.Len(t, s, 1) s = set1.Join(set2...) require.Len(t, s, 3) s = s.Join(set1...).Join(set2...) require.Len(t, s, 3) } func Test_TaggedFuncs(t *testing.T) { a := func() {} b := func() {} set1 := TaggedValueSet[any]{TaggedValue[any]{Value: a}} set2 := TaggedValueSet[any]{TaggedValue[any]{Value: b}} s := set2.Join(set2...) require.Len(t, s, 1) s = set1.Join(set2...) require.Len(t, s, 2) s = s.Join(set1...).Join(set2...) require.Len(t, s, 2) } func Test_Tagged(t *testing.T) { set := TaggedValueSet[int]{ NewTaggedValue(1, "one"), NewTaggedValue(2, "two", "second"), NewTaggedValue(3, "three", "third"), NewTaggedValue(23, "twenty-three", "twenty", "third"), NewTaggedValue(4, "four", ""), NewTaggedValue(9, "nine"), } tests := []struct { name string keep []string remove []string expected []int }{ { name: "by tag", keep: arr("two"), expected: arr(2), }, { name: "by multiple", keep: arr("one", "third"), expected: arr(1, 3, 23), }, { name: "nil keep", keep: nil, expected: nil, }, { name: "empty keep", keep: []string{}, expected: nil, }, { name: "remove by tag", keep: arr("one", "twenty-three"), remove: arr("third"), expected: arr(1), }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { got := set.Select(test.keep...).Remove(test.remove...) if test.expected == nil { require.Empty(t, got) return } require.ElementsMatch(t, test.expected, got.Values()) }) } } func Test_Tags(t *testing.T) { values := TaggedValueSet[int]{ NewTaggedValue(10, "one", "zero", "ten"), NewTaggedValue(11, "one", "eleven"), NewTaggedValue(20, "two", "zero", "twenty"), NewTaggedValue(22, "two", "twenty two"), } expected := []string{"one", "zero", "ten", "eleven", "two", "twenty", "twenty two"} require.Equal(t, expected, values.Tags()) } func Test_twoStepFilter(t *testing.T) { tag := func(name string, tags ...string) TaggedValue[string] { return NewTaggedValue(name, append([]string{name}, tags...)...) } all := TaggedValueSet[string]{ tag("js-1", "i", "js"), tag("js-2", "d", "js"), tag("js-3", "i", "d", "js"), tag("jv-1", "i", "d", "jv"), tag("jv-2", "i", "d", "jv"), tag("py-1", "i", "d", "py"), tag("py-2", "d", "py"), tag("py-3", "i", "d", "py"), tag("py-4", "i", "py"), tag("sc-1"), } tests := []struct { name string req filterRequest expected []string }{ { name: "--override-default-catalogers i", req: filterRequest{ Base: str("i"), }, expected: str("js-1", "js-3", "jv-1", "jv-2", "py-1", "py-3", "py-4"), }, { name: "--override-default-catalogers i,js", req: filterRequest{ Base: str("i", "js"), }, expected: str("js-1", "js-2", "js-3", "jv-1", "jv-2", "py-1", "py-3", "py-4"), }, { name: "--select-catalogers “+javascript” [ERROR]", req: filterRequest{ Base: str("i"), Add: str("js"), }, expected: str("js-1", "js-3", "jv-1", "jv-2", "py-1", "py-3", "py-4", "js-2"), }, { name: "--select-catalogers +sc-1", req: filterRequest{ Add: str("sc-1"), }, expected: str("js-1", "js-2", "js-3", "jv-1", "jv-2", "py-1", "py-2", "py-3", "py-4", "sc-1"), }, { name: "--select-catalogers -py-1", req: filterRequest{ Remove: str("py-1"), }, expected: str("js-1", "js-2", "js-3", "jv-1", "jv-2", "py-2", "py-3", "py-4", "sc-1"), }, { name: "--select-catalogers js", req: filterRequest{ Select: str("js"), }, expected: str("js-1", "js-2", "js-3"), }, { name: "--override-default-catalogers d --select-catalogers py,js", req: filterRequest{ Base: str("d"), Select: str("py", "js"), }, expected: str("js-2", "js-3", "py-1", "py-2", "py-3"), }, { name: "--select-catalogers -py-1,-py-2,+sc-1,+js-2", req: filterRequest{ Base: str("i"), Add: str("sc-1", "js-2"), Remove: str("py-1", "py-2"), }, expected: str("js-1", "js-3", "jv-1", "jv-2", "py-3", "py-4", "js-2", "sc-1"), }, { name: "--select-catalogers js,py", req: filterRequest{ Base: str("i"), Select: str("js", "py"), }, expected: str("js-1", "js-3", "py-1", "py-3", "py-4"), }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { got := twoLevelFilter(all, test.req) require.ElementsMatch(t, test.expected, got) }) } } type filterRequest struct { Base []string Select []string Remove []string Add []string } func twoLevelFilter[T comparable](allValues TaggedValueSet[T], r filterRequest) []T { values := allValues if len(r.Base) > 0 { values = values.Select(r.Base...) } if len(r.Select) > 0 { values = values.Select(r.Select...) } if len(r.Remove) > 0 { values = values.Remove(r.Remove...) } if len(r.Add) > 0 { values = values.Join(allValues.Select(r.Add...)...) } return values.Values() } func str(s ...string) []string { return s } func arr[T any](v ...T) []T { return v }