From d2719b75e6df1157c33377ff82da0e67fc2b68c9 Mon Sep 17 00:00:00 2001 From: asomera Date: Fri, 27 Mar 2026 10:13:17 -0700 Subject: [PATCH 1/2] chore: upgrade to sdk v4; add ci --- .github/workflows/ci.yml | 33 + .gitignore | 1 + go.mod | 2 +- go.sum | 4 +- internal/cli/cli.go | 17 +- vendor/github.com/alecthomas/kong/.gitignore | 0 .../github.com/alecthomas/kong/.golangci.yml | 85 - vendor/github.com/alecthomas/kong/COPYING | 19 - vendor/github.com/alecthomas/kong/README.md | 775 -- vendor/github.com/alecthomas/kong/build.go | 411 - .../github.com/alecthomas/kong/callbacks.go | 231 - .../github.com/alecthomas/kong/camelcase.go | 90 - vendor/github.com/alecthomas/kong/context.go | 1183 -- vendor/github.com/alecthomas/kong/defaults.go | 21 - vendor/github.com/alecthomas/kong/doc.go | 32 - vendor/github.com/alecthomas/kong/error.go | 21 - vendor/github.com/alecthomas/kong/exit.go | 32 - vendor/github.com/alecthomas/kong/global.go | 16 - .../github.com/alecthomas/kong/guesswidth.go | 10 - .../alecthomas/kong/guesswidth_unix.go | 42 - vendor/github.com/alecthomas/kong/help.go | 576 - vendor/github.com/alecthomas/kong/hooks.go | 32 - .../github.com/alecthomas/kong/interpolate.go | 52 - vendor/github.com/alecthomas/kong/kong.go | 500 - vendor/github.com/alecthomas/kong/kong.png | Bin 68610 -> 0 bytes vendor/github.com/alecthomas/kong/kong.sketch | Bin 162506 -> 0 bytes .../github.com/alecthomas/kong/lefthook.yml | 11 - .../github.com/alecthomas/kong/levenshtein.go | 39 - vendor/github.com/alecthomas/kong/mapper.go | 934 -- vendor/github.com/alecthomas/kong/model.go | 514 - .../github.com/alecthomas/kong/negatable.go | 19 - vendor/github.com/alecthomas/kong/options.go | 563 - .../github.com/alecthomas/kong/renovate.json5 | 18 - vendor/github.com/alecthomas/kong/resolver.go | 68 - vendor/github.com/alecthomas/kong/scanner.go | 236 - vendor/github.com/alecthomas/kong/tag.go | 386 - vendor/github.com/alecthomas/kong/util.go | 57 - vendor/github.com/alecthomas/kong/visit.go | 58 - .../anduril/lattice-sdk-go/v2/.fernignore | 4 - .../anduril/lattice-sdk-go/v2/LICENSE | 189 - .../anduril/lattice-sdk-go/v2/README.md | 164 - .../anduril/lattice-sdk-go/v2/SECURITY.md | 5 - .../lattice-sdk-go/v2/client/client.go | 40 - .../lattice-sdk-go/v2/core/api_error.go | 47 - .../anduril/lattice-sdk-go/v2/core/http.go | 15 - .../anduril/lattice-sdk-go/v2/core/page.go | 70 - .../lattice-sdk-go/v2/core/request_option.go | 125 - .../anduril/lattice-sdk-go/v2/core/stream.go | 328 - .../anduril/lattice-sdk-go/v2/entities.go | 532 - .../lattice-sdk-go/v2/entities/client.go | 201 - .../anduril/lattice-sdk-go/v2/entities/doc.go | 4 - .../lattice-sdk-go/v2/entities/raw_client.go | 350 - .../anduril/lattice-sdk-go/v2/environments.go | 13 - .../anduril/lattice-sdk-go/v2/errors.go | 202 - .../anduril/lattice-sdk-go/v2/file_param.go | 41 - .../lattice-sdk-go/v2/internal/caller.go | 250 - .../v2/internal/error_decoder.go | 47 - .../v2/internal/extra_properties.go | 141 - .../lattice-sdk-go/v2/internal/http.go | 48 - .../lattice-sdk-go/v2/internal/pager.go | 127 - .../lattice-sdk-go/v2/internal/query.go | 300 - .../lattice-sdk-go/v2/internal/retrier.go | 165 - .../lattice-sdk-go/v2/internal/streamer.go | 118 - .../lattice-sdk-go/v2/internal/stringer.go | 13 - .../lattice-sdk-go/v2/internal/time.go | 137 - .../anduril/lattice-sdk-go/v2/objects.go | 254 - .../lattice-sdk-go/v2/objects/client.go | 188 - .../anduril/lattice-sdk-go/v2/objects/doc.go | 4 - .../lattice-sdk-go/v2/objects/raw_client.go | 304 - .../v2/option/request_option.go | 71 - .../anduril/lattice-sdk-go/v2/pointer.go | 132 - .../anduril/lattice-sdk-go/v2/tasks.go | 1574 --- .../anduril/lattice-sdk-go/v2/tasks/client.go | 126 - .../anduril/lattice-sdk-go/v2/tasks/doc.go | 4 - .../lattice-sdk-go/v2/tasks/raw_client.go | 328 - .../anduril/lattice-sdk-go/v2/types.go | 9551 ----------------- vendor/github.com/google/uuid/CHANGELOG.md | 41 - vendor/github.com/google/uuid/CONTRIBUTING.md | 26 - vendor/github.com/google/uuid/CONTRIBUTORS | 9 - vendor/github.com/google/uuid/LICENSE | 27 - vendor/github.com/google/uuid/README.md | 21 - vendor/github.com/google/uuid/dce.go | 80 - vendor/github.com/google/uuid/doc.go | 12 - vendor/github.com/google/uuid/hash.go | 59 - vendor/github.com/google/uuid/marshal.go | 38 - vendor/github.com/google/uuid/node.go | 90 - vendor/github.com/google/uuid/node_js.go | 12 - vendor/github.com/google/uuid/node_net.go | 33 - vendor/github.com/google/uuid/null.go | 118 - vendor/github.com/google/uuid/sql.go | 59 - vendor/github.com/google/uuid/time.go | 134 - vendor/github.com/google/uuid/util.go | 43 - vendor/github.com/google/uuid/uuid.go | 365 - vendor/github.com/google/uuid/version1.go | 44 - vendor/github.com/google/uuid/version4.go | 76 - vendor/github.com/google/uuid/version6.go | 56 - vendor/github.com/google/uuid/version7.go | 104 - vendor/modules.txt | 22 - 98 files changed, 45 insertions(+), 24724 deletions(-) create mode 100644 .github/workflows/ci.yml delete mode 100644 vendor/github.com/alecthomas/kong/.gitignore delete mode 100644 vendor/github.com/alecthomas/kong/.golangci.yml delete mode 100644 vendor/github.com/alecthomas/kong/COPYING delete mode 100644 vendor/github.com/alecthomas/kong/README.md delete mode 100644 vendor/github.com/alecthomas/kong/build.go delete mode 100644 vendor/github.com/alecthomas/kong/callbacks.go delete mode 100644 vendor/github.com/alecthomas/kong/camelcase.go delete mode 100644 vendor/github.com/alecthomas/kong/context.go delete mode 100644 vendor/github.com/alecthomas/kong/defaults.go delete mode 100644 vendor/github.com/alecthomas/kong/doc.go delete mode 100644 vendor/github.com/alecthomas/kong/error.go delete mode 100644 vendor/github.com/alecthomas/kong/exit.go delete mode 100644 vendor/github.com/alecthomas/kong/global.go delete mode 100644 vendor/github.com/alecthomas/kong/guesswidth.go delete mode 100644 vendor/github.com/alecthomas/kong/guesswidth_unix.go delete mode 100644 vendor/github.com/alecthomas/kong/help.go delete mode 100644 vendor/github.com/alecthomas/kong/hooks.go delete mode 100644 vendor/github.com/alecthomas/kong/interpolate.go delete mode 100644 vendor/github.com/alecthomas/kong/kong.go delete mode 100644 vendor/github.com/alecthomas/kong/kong.png delete mode 100644 vendor/github.com/alecthomas/kong/kong.sketch delete mode 100644 vendor/github.com/alecthomas/kong/lefthook.yml delete mode 100644 vendor/github.com/alecthomas/kong/levenshtein.go delete mode 100644 vendor/github.com/alecthomas/kong/mapper.go delete mode 100644 vendor/github.com/alecthomas/kong/model.go delete mode 100644 vendor/github.com/alecthomas/kong/negatable.go delete mode 100644 vendor/github.com/alecthomas/kong/options.go delete mode 100644 vendor/github.com/alecthomas/kong/renovate.json5 delete mode 100644 vendor/github.com/alecthomas/kong/resolver.go delete mode 100644 vendor/github.com/alecthomas/kong/scanner.go delete mode 100644 vendor/github.com/alecthomas/kong/tag.go delete mode 100644 vendor/github.com/alecthomas/kong/util.go delete mode 100644 vendor/github.com/alecthomas/kong/visit.go delete mode 100644 vendor/github.com/anduril/lattice-sdk-go/v2/.fernignore delete mode 100644 vendor/github.com/anduril/lattice-sdk-go/v2/LICENSE delete mode 100644 vendor/github.com/anduril/lattice-sdk-go/v2/README.md delete mode 100644 vendor/github.com/anduril/lattice-sdk-go/v2/SECURITY.md delete mode 100644 vendor/github.com/anduril/lattice-sdk-go/v2/client/client.go delete mode 100644 vendor/github.com/anduril/lattice-sdk-go/v2/core/api_error.go delete mode 100644 vendor/github.com/anduril/lattice-sdk-go/v2/core/http.go delete mode 100644 vendor/github.com/anduril/lattice-sdk-go/v2/core/page.go delete mode 100644 vendor/github.com/anduril/lattice-sdk-go/v2/core/request_option.go delete mode 100644 vendor/github.com/anduril/lattice-sdk-go/v2/core/stream.go delete mode 100644 vendor/github.com/anduril/lattice-sdk-go/v2/entities.go delete mode 100644 vendor/github.com/anduril/lattice-sdk-go/v2/entities/client.go delete mode 100644 vendor/github.com/anduril/lattice-sdk-go/v2/entities/doc.go delete mode 100644 vendor/github.com/anduril/lattice-sdk-go/v2/entities/raw_client.go delete mode 100644 vendor/github.com/anduril/lattice-sdk-go/v2/environments.go delete mode 100644 vendor/github.com/anduril/lattice-sdk-go/v2/errors.go delete mode 100644 vendor/github.com/anduril/lattice-sdk-go/v2/file_param.go delete mode 100644 vendor/github.com/anduril/lattice-sdk-go/v2/internal/caller.go delete mode 100644 vendor/github.com/anduril/lattice-sdk-go/v2/internal/error_decoder.go delete mode 100644 vendor/github.com/anduril/lattice-sdk-go/v2/internal/extra_properties.go delete mode 100644 vendor/github.com/anduril/lattice-sdk-go/v2/internal/http.go delete mode 100644 vendor/github.com/anduril/lattice-sdk-go/v2/internal/pager.go delete mode 100644 vendor/github.com/anduril/lattice-sdk-go/v2/internal/query.go delete mode 100644 vendor/github.com/anduril/lattice-sdk-go/v2/internal/retrier.go delete mode 100644 vendor/github.com/anduril/lattice-sdk-go/v2/internal/streamer.go delete mode 100644 vendor/github.com/anduril/lattice-sdk-go/v2/internal/stringer.go delete mode 100644 vendor/github.com/anduril/lattice-sdk-go/v2/internal/time.go delete mode 100644 vendor/github.com/anduril/lattice-sdk-go/v2/objects.go delete mode 100644 vendor/github.com/anduril/lattice-sdk-go/v2/objects/client.go delete mode 100644 vendor/github.com/anduril/lattice-sdk-go/v2/objects/doc.go delete mode 100644 vendor/github.com/anduril/lattice-sdk-go/v2/objects/raw_client.go delete mode 100644 vendor/github.com/anduril/lattice-sdk-go/v2/option/request_option.go delete mode 100644 vendor/github.com/anduril/lattice-sdk-go/v2/pointer.go delete mode 100644 vendor/github.com/anduril/lattice-sdk-go/v2/tasks.go delete mode 100644 vendor/github.com/anduril/lattice-sdk-go/v2/tasks/client.go delete mode 100644 vendor/github.com/anduril/lattice-sdk-go/v2/tasks/doc.go delete mode 100644 vendor/github.com/anduril/lattice-sdk-go/v2/tasks/raw_client.go delete mode 100644 vendor/github.com/anduril/lattice-sdk-go/v2/types.go delete mode 100644 vendor/github.com/google/uuid/CHANGELOG.md delete mode 100644 vendor/github.com/google/uuid/CONTRIBUTING.md delete mode 100644 vendor/github.com/google/uuid/CONTRIBUTORS delete mode 100644 vendor/github.com/google/uuid/LICENSE delete mode 100644 vendor/github.com/google/uuid/README.md delete mode 100644 vendor/github.com/google/uuid/dce.go delete mode 100644 vendor/github.com/google/uuid/doc.go delete mode 100644 vendor/github.com/google/uuid/hash.go delete mode 100644 vendor/github.com/google/uuid/marshal.go delete mode 100644 vendor/github.com/google/uuid/node.go delete mode 100644 vendor/github.com/google/uuid/node_js.go delete mode 100644 vendor/github.com/google/uuid/node_net.go delete mode 100644 vendor/github.com/google/uuid/null.go delete mode 100644 vendor/github.com/google/uuid/sql.go delete mode 100644 vendor/github.com/google/uuid/time.go delete mode 100644 vendor/github.com/google/uuid/util.go delete mode 100644 vendor/github.com/google/uuid/uuid.go delete mode 100644 vendor/github.com/google/uuid/version1.go delete mode 100644 vendor/github.com/google/uuid/version4.go delete mode 100644 vendor/github.com/google/uuid/version6.go delete mode 100644 vendor/github.com/google/uuid/version7.go delete mode 100644 vendor/modules.txt diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..ade399f --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,33 @@ +name: CI + +on: + push: + branches: [master] + pull_request: + branches: [master] + +permissions: + contents: read + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-go@v5 + with: + go-version-file: go.mod + + - name: Build + run: go build ./... + + - name: Lint + uses: golangci/golangci-lint-action@v7 + with: + version: v2.11 + + - name: Check module tidiness + run: | + go mod tidy + git diff --exit-code go.mod go.sum diff --git a/.gitignore b/.gitignore index 6c50c96..f5d42d0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ build/ +vendor/ .idea/ .vscode/ .DS_Store diff --git a/go.mod b/go.mod index f593a68..5f50e38 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ toolchain go1.23.4 require ( github.com/alecthomas/kong v1.11.0 - github.com/anduril/lattice-sdk-go/v2 v2.2.0 + github.com/anduril/lattice-sdk-go/v4 v4.5.0 ) require ( diff --git a/go.sum b/go.sum index 5f79dfb..ad74332 100644 --- a/go.sum +++ b/go.sum @@ -4,8 +4,8 @@ github.com/alecthomas/kong v1.11.0 h1:y++1gI7jf8O7G7l4LZo5ASFhrhJvzc+WgF/arranEm github.com/alecthomas/kong v1.11.0/go.mod h1:p2vqieVMeTAnaC83txKtXe8FLke2X07aruPWXyMPQrU= github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc= github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= -github.com/anduril/lattice-sdk-go/v2 v2.2.0 h1:7qEveGHtCoKd81of9PjPkNMLMCVMXMiJKwSVvavQIrY= -github.com/anduril/lattice-sdk-go/v2 v2.2.0/go.mod h1:LZXFUrBP6Zuq+IhNpqN1tiaMuC2WayvxlGVO+ApesKo= +github.com/anduril/lattice-sdk-go/v4 v4.5.0 h1:miiQk94+fRa5ZqokWy4uZL533/4qDGxbMOXYS262E4k= +github.com/anduril/lattice-sdk-go/v4 v4.5.0/go.mod h1:bGvU1erf9umZBBtdWyJn2mGtHPCx5g4mub97naVrOmM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= diff --git a/internal/cli/cli.go b/internal/cli/cli.go index fbca0e6..cdde387 100644 --- a/internal/cli/cli.go +++ b/internal/cli/cli.go @@ -12,9 +12,9 @@ import ( "time" "github.com/alecthomas/kong" - api "github.com/anduril/lattice-sdk-go/v2" - client "github.com/anduril/lattice-sdk-go/v2/client" - option "github.com/anduril/lattice-sdk-go/v2/option" + api "github.com/anduril/lattice-sdk-go/v4" + client "github.com/anduril/lattice-sdk-go/v4/client" + option "github.com/anduril/lattice-sdk-go/v4/option" ) type cli struct { @@ -36,7 +36,7 @@ func (d *deleteCmd) Run(kongCtx *kong.Context) error { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - if err := objectStoreClient.Objects.DeleteObject(ctx, d.Path); err != nil { + if err := objectStoreClient.Objects.DeleteObject(ctx, &api.DeleteObjectRequest{ObjectPath: d.Path}); err != nil { return fmt.Errorf("unable to delete path %q: %w", d.Path, err) } fmt.Printf("deleted path %q\n", d.Path) @@ -77,7 +77,7 @@ func (u *uploadCmd) Run(kongCtx *kong.Context) error { if err != nil { return fmt.Errorf("unable to open file %q: %w", u.InputPath, err) } - defer fileReader.Close() + defer func() { _ = fileReader.Close() }() fileBytes, err := io.ReadAll(fileReader) if err != nil { @@ -107,7 +107,7 @@ func (o *objectMetadataCmd) Run(kongCtx *kong.Context) error { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - header, err := objectStoreClient.Objects.WithRawResponse.GetObjectMetadata(ctx, o.Path) + header, err := objectStoreClient.Objects.WithRawResponse.GetObjectMetadata(ctx, &api.GetObjectMetadataRequest{ObjectPath: o.Path}) if err != nil { return fmt.Errorf("unable to get object metadata for path %q, err=%w", o.Path, err) } @@ -132,8 +132,7 @@ func (o *getCmd) Run(kongCtx *kong.Context) error { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - request := &api.GetObjectRequest{} - objectReader, err := objectStoreClient.Objects.GetObject(ctx, o.ObjectStorePath, request) + objectReader, err := objectStoreClient.Objects.GetObject(ctx, &api.GetObjectRequest{ObjectPath: o.ObjectStorePath}) if err != nil { return fmt.Errorf("unable to get file %q from object store: %w", o.ObjectStorePath, err) } @@ -142,7 +141,7 @@ func (o *getCmd) Run(kongCtx *kong.Context) error { if err != nil { return fmt.Errorf("unable to create writer for %q: %w", o.OutputPath, err) } - defer outputWriter.Close() + defer func() { _ = outputWriter.Close() }() bytesCopied, err := io.Copy(outputWriter, objectReader) if err != nil { diff --git a/vendor/github.com/alecthomas/kong/.gitignore b/vendor/github.com/alecthomas/kong/.gitignore deleted file mode 100644 index e69de29..0000000 diff --git a/vendor/github.com/alecthomas/kong/.golangci.yml b/vendor/github.com/alecthomas/kong/.golangci.yml deleted file mode 100644 index 1eb0b92..0000000 --- a/vendor/github.com/alecthomas/kong/.golangci.yml +++ /dev/null @@ -1,85 +0,0 @@ -run: - tests: true - -output: - print-issued-lines: false - -linters: - enable-all: true - disable: - - lll - - gochecknoglobals - - wsl - - funlen - - gocognit - - goprintffuncname - - paralleltest - - nlreturn - - testpackage - - wrapcheck - - forbidigo - - gci - - godot - - gofumpt - - cyclop - - errorlint - - nestif - - tagliatelle - - thelper - - godox - - goconst - - varnamelen - - ireturn - - exhaustruct - - nonamedreturns - - nilnil - - depguard # nothing to guard against yet - - tagalign # hurts readability of kong tags - - tenv # deprecated since v1.64, but not removed yet - - mnd - - perfsprint - - err113 - - copyloopvar - - intrange - - nakedret - - recvcheck # value receivers are intentionally used for copies - -linters-settings: - govet: - # These govet checks are disabled by default, but they're useful. - enable: - - niliness - - sortslice - - unusedwrite - dupl: - threshold: 100 - gocyclo: - min-complexity: 20 - exhaustive: - default-signifies-exhaustive: true - -issues: - max-per-linter: 0 - max-same: 0 - exclude-use-default: false - exclude: - - '^(G104|G204):' - # Very commonly not checked. - - 'Error return value of .(.*\.Help|.*\.MarkFlagRequired|(os\.)?std(out|err)\..*|.*Close|.*Flush|os\.Remove(All)?|.*printf?|os\.(Un)?Setenv). is not checked' - - 'exported method (.*\.MarshalJSON|.*\.UnmarshalJSON) should have comment or be unexported' - - 'composite literal uses unkeyed fields' - - 'bad syntax for struct tag key' - - 'bad syntax for struct tag pair' - - 'result .* \(error\) is always nil' - - 'Error return value of `fmt.Fprintln` is not checked' - - exclude-rules: - # Don't warn on unused parameters. - # Parameter names are useful for documentation. - # Replacing them with '_' hides useful information. - - linters: [revive] - text: 'unused-parameter: parameter \S+ seems to be unused, consider removing or renaming it as _' - - # Duplicate words are okay in tests. - - linters: [dupword] - path: _test\.go diff --git a/vendor/github.com/alecthomas/kong/COPYING b/vendor/github.com/alecthomas/kong/COPYING deleted file mode 100644 index 22707ac..0000000 --- a/vendor/github.com/alecthomas/kong/COPYING +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (C) 2018 Alec Thomas - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do -so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/vendor/github.com/alecthomas/kong/README.md b/vendor/github.com/alecthomas/kong/README.md deleted file mode 100644 index a976332..0000000 --- a/vendor/github.com/alecthomas/kong/README.md +++ /dev/null @@ -1,775 +0,0 @@ - -

- -# Kong is a command-line parser for Go - -[![](https://godoc.org/github.com/alecthomas/kong?status.svg)](http://godoc.org/github.com/alecthomas/kong) [![CircleCI](https://img.shields.io/circleci/project/github/alecthomas/kong.svg)](https://circleci.com/gh/alecthomas/kong) [![Go Report Card](https://goreportcard.com/badge/github.com/alecthomas/kong)](https://goreportcard.com/report/github.com/alecthomas/kong) [![Slack chat](https://img.shields.io/static/v1?logo=slack&style=flat&label=slack&color=green&message=gophers)](https://gophers.slack.com/messages/CN9DS8YF3) - -- [Version 1.0.0 Release](#version-100-release) -- [Introduction](#introduction) -- [Help](#help) - - [Help as a user of a Kong application](#help-as-a-user-of-a-kong-application) - - [Defining help in Kong](#defining-help-in-kong) -- [Command handling](#command-handling) - - [Switch on the command string](#switch-on-the-command-string) - - [Attach a `Run(...) error` method to each command](#attach-a-run-error-method-to-each-command) -- [Hooks: BeforeReset(), BeforeResolve(), BeforeApply(), AfterApply()](#hooks-beforereset-beforeresolve-beforeapply-afterapply) -- [The Bind() option](#the-bind-option) -- [Flags](#flags) -- [Commands and sub-commands](#commands-and-sub-commands) -- [Branching positional arguments](#branching-positional-arguments) -- [Positional arguments](#positional-arguments) -- [Slices](#slices) -- [Maps](#maps) -- [Pointers](#pointers) -- [Nested data structure](#nested-data-structure) -- [Custom named decoders](#custom-named-decoders) -- [Supported field types](#supported-field-types) -- [Custom decoders (mappers)](#custom-decoders-mappers) -- [Supported tags](#supported-tags) -- [Plugins](#plugins) -- [Dynamic Commands](#dynamic-commands) -- [Variable interpolation](#variable-interpolation) -- [Validation](#validation) -- [Modifying Kong's behaviour](#modifying-kongs-behaviour) - - [`Name(help)` and `Description(help)` - set the application name description](#namehelp-and-descriptionhelp---set-the-application-name-description) - - [`Configuration(loader, paths...)` - load defaults from configuration files](#configurationloader-paths---load-defaults-from-configuration-files) - - [`Resolver(...)` - support for default values from external sources](#resolver---support-for-default-values-from-external-sources) - - [`*Mapper(...)` - customising how the command-line is mapped to Go values](#mapper---customising-how-the-command-line-is-mapped-to-go-values) - - [`ConfigureHelp(HelpOptions)` and `Help(HelpFunc)` - customising help](#configurehelphelpoptions-and-helphelpfunc---customising-help) - - [Injecting values into `Run()` methods](#injecting-values-into-run-methods) - - [Other options](#other-options) - -## Version 1.0.0 Release - -Kong has been stable for a long time, so it seemed appropriate to cut a 1.0 release. - -There is one breaking change, [#436](https://github.com/alecthomas/kong/pull/436), which should effect relatively few users. - -## Introduction - -Kong aims to support arbitrarily complex command-line structures with as little developer effort as possible. - -To achieve that, command-lines are expressed as Go types, with the structure and tags directing how the command line is mapped onto the struct. - -For example, the following command-line: - - shell rm [-f] [-r] ... - shell ls [ ...] - -Can be represented by the following command-line structure: - -```go -package main - -import "github.com/alecthomas/kong" - -var CLI struct { - Rm struct { - Force bool `help:"Force removal."` - Recursive bool `help:"Recursively remove files."` - - Paths []string `arg:"" name:"path" help:"Paths to remove." type:"path"` - } `cmd:"" help:"Remove files."` - - Ls struct { - Paths []string `arg:"" optional:"" name:"path" help:"Paths to list." type:"path"` - } `cmd:"" help:"List paths."` -} - -func main() { - ctx := kong.Parse(&CLI) - switch ctx.Command() { - case "rm ": - case "ls": - default: - panic(ctx.Command()) - } -} -``` - -## Help - -### Help as a user of a Kong application - -Every Kong application includes a `--help` flag that will display auto-generated help. - -eg. - - $ shell --help - usage: shell - - A shell-like example app. - - Flags: - --help Show context-sensitive help. - --debug Debug mode. - - Commands: - rm ... - Remove files. - - ls [ ...] - List paths. - -If a command is provided, the help will show full detail on the command including all available flags. - -eg. - - $ shell --help rm - usage: shell rm ... - - Remove files. - - Arguments: - ... Paths to remove. - - Flags: - --debug Debug mode. - - -f, --force Force removal. - -r, --recursive Recursively remove files. - -### Defining help in Kong - -Help is automatically generated from the command-line structure itself, -including `help:""` and other tags. [Variables](#variable-interpolation) will -also be interpolated into the help string. - -Finally, any command, or argument type implementing the interface -`Help() string` will have this function called to retrieve more detail to -augment the help tag. This allows for much more descriptive text than can -fit in Go tags. [See \_examples/shell/help](./_examples/shell/help) - -#### Showing the _command_'s detailed help - -A command's additional help text is _not_ shown from top-level help, but can be displayed within contextual help: - -**Top level help** - -```bash - $ go run ./_examples/shell/help --help -Usage: help - -An app demonstrating HelpProviders - -Flags: - -h, --help Show context-sensitive help. - --flag Regular flag help - -Commands: - echo Regular command help -``` - -**Contextual** - -```bash - $ go run ./_examples/shell/help echo --help -Usage: help echo - -Regular command help - -🚀 additional command help - -Arguments: - Regular argument help - -Flags: - -h, --help Show context-sensitive help. - --flag Regular flag help -``` - -#### Showing an _argument_'s detailed help - -Custom help will only be shown for _positional arguments with named fields_ ([see the README section on positional arguments for more details on what that means](#branching-positional-arguments)) - -**Contextual argument help** - -```bash - $ go run ./_examples/shell/help msg --help -Usage: help echo - -Regular argument help - -📣 additional argument help - -Flags: - -h, --help Show context-sensitive help. - --flag Regular flag help -``` - -## Command handling - -There are two ways to handle commands in Kong. - -### Switch on the command string - -When you call `kong.Parse()` it will return a unique string representation of the command. Each command branch in the hierarchy will be a bare word and each branching argument or required positional argument will be the name surrounded by angle brackets. Here's an example: - -There's an example of this pattern [here](https://github.com/alecthomas/kong/blob/master/_examples/shell/commandstring/main.go). - -eg. - -```go -package main - -import "github.com/alecthomas/kong" - -var CLI struct { - Rm struct { - Force bool `help:"Force removal."` - Recursive bool `help:"Recursively remove files."` - - Paths []string `arg:"" name:"path" help:"Paths to remove." type:"path"` - } `cmd:"" help:"Remove files."` - - Ls struct { - Paths []string `arg:"" optional:"" name:"path" help:"Paths to list." type:"path"` - } `cmd:"" help:"List paths."` -} - -func main() { - ctx := kong.Parse(&CLI) - switch ctx.Command() { - case "rm ": - case "ls": - default: - panic(ctx.Command()) - } -} -``` - -This has the advantage that it is convenient, but the downside that if you modify your CLI structure, the strings may change. This can be fragile. - -### Attach a `Run(...) error` method to each command - -A more robust approach is to break each command out into their own structs: - -1. Break leaf commands out into separate structs. -2. Attach a `Run(...) error` method to all leaf commands. -3. Call `kong.Kong.Parse()` to obtain a `kong.Context`. -4. Call `kong.Context.Run(bindings...)` to call the selected parsed command. - -Once a command node is selected by Kong it will search from that node back to the root. Each -encountered command node with a `Run(...) error` will be called in reverse order. This allows -sub-trees to be re-used fairly conveniently. - -In addition to values bound with the `kong.Bind(...)` option, any values -passed through to `kong.Context.Run(...)` are also bindable to the target's -`Run()` arguments. - -Finally, hooks can also contribute bindings via `kong.Context.Bind()` and `kong.Context.BindTo()`. - -There's a full example emulating part of the Docker CLI [here](https://github.com/alecthomas/kong/tree/master/_examples/docker). - -eg. - -```go -type Context struct { - Debug bool -} - -type RmCmd struct { - Force bool `help:"Force removal."` - Recursive bool `help:"Recursively remove files."` - - Paths []string `arg:"" name:"path" help:"Paths to remove." type:"path"` -} - -func (r *RmCmd) Run(ctx *Context) error { - fmt.Println("rm", r.Paths) - return nil -} - -type LsCmd struct { - Paths []string `arg:"" optional:"" name:"path" help:"Paths to list." type:"path"` -} - -func (l *LsCmd) Run(ctx *Context) error { - fmt.Println("ls", l.Paths) - return nil -} - -var cli struct { - Debug bool `help:"Enable debug mode."` - - Rm RmCmd `cmd:"" help:"Remove files."` - Ls LsCmd `cmd:"" help:"List paths."` -} - -func main() { - ctx := kong.Parse(&cli) - // Call the Run() method of the selected parsed command. - err := ctx.Run(&Context{Debug: cli.Debug}) - ctx.FatalIfErrorf(err) -} - -``` - -## Hooks: BeforeReset(), BeforeResolve(), BeforeApply(), AfterApply() - -If a node in the CLI, or any of its embedded fields, implements a `BeforeReset(...) error`, `BeforeResolve -(...) error`, `BeforeApply(...) error` and/or `AfterApply(...) error` method, those will be called as Kong -resets, resolves, validates, and assigns values to the node. - -| Hook | Description | -| --------------- | ----------------------------------------------------------------------------------------------------------- | -| `BeforeReset` | Invoked before values are reset to their defaults (as defined by the grammar) or to zero values | -| `BeforeResolve` | Invoked before resolvers are applied to a node | -| `BeforeApply` | Invoked before the traced command line arguments are applied to the grammar | -| `AfterApply` | Invoked after command line arguments are applied to the grammar **and validated**` | - -The `--help` flag is implemented with a `BeforeReset` hook. - -eg. - -```go -// A flag with a hook that, if triggered, will set the debug loggers output to stdout. -type debugFlag bool - -func (d debugFlag) BeforeApply(logger *log.Logger) error { - logger.SetOutput(os.Stdout) - return nil -} - -var cli struct { - Debug debugFlag `help:"Enable debug logging."` -} - -func main() { - // Debug logger going to discard. - logger := log.New(io.Discard, "", log.LstdFlags) - - ctx := kong.Parse(&cli, kong.Bind(logger)) - - // ... -} -``` - -It's also possible to register these hooks with the functional options -`kong.WithBeforeReset`, `kong.WithBeforeResolve`, `kong.WithBeforeApply`, and -`kong.WithAfterApply`. - -## The Bind() option - -Arguments to hooks are provided via the `Run(...)` method or `Bind(...)` option. `*Kong`, `*Context`, `*Path` and parent commands are also bound and finally, hooks can also contribute bindings via `kong.Context.Bind()` and `kong.Context.BindTo()`. - -eg: - -```go -type CLI struct { - Debug bool `help:"Enable debug mode."` - - Rm RmCmd `cmd:"" help:"Remove files."` - Ls LsCmd `cmd:"" help:"List paths."` -} - -type AuthorName string - -// ... -func (l *LsCmd) Run(cli *CLI) error { -// use cli.Debug here !! - return nil -} - -func (r *RmCmD) Run(author AuthorName) error{ -// use binded author here - return nil -} - -func main() { - var cli CLI - - ctx := kong.Parse(&cli, Bind(AuthorName("penguin"))) - err := ctx.Run() -``` - -## Flags - -Any [mapped](#mapper---customising-how-the-command-line-is-mapped-to-go-values) field in the command structure _not_ tagged with `cmd` or `arg` will be a flag. Flags are optional by default. - -eg. The command-line `app [--flag="foo"]` can be represented by the following. - -```go -type CLI struct { - Flag string -} -``` - -## Commands and sub-commands - -Sub-commands are specified by tagging a struct field with `cmd`. Kong supports arbitrarily nested commands. - -eg. The following struct represents the CLI structure `command [--flag="str"] sub-command`. - -```go -type CLI struct { - Command struct { - Flag string - - SubCommand struct { - } `cmd` - } `cmd` -} -``` - -If a sub-command is tagged with `default:"1"` it will be selected if there are no further arguments. If a sub-command is tagged with `default:"withargs"` it will be selected even if there are further arguments or flags and those arguments or flags are valid for the sub-command. This allows the user to omit the sub-command name on the CLI if its arguments/flags are not ambiguous with the sibling commands or flags. - -## Branching positional arguments - -In addition to sub-commands, structs can also be configured as branching positional arguments. - -This is achieved by tagging an [unmapped](#mapper---customising-how-the-command-line-is-mapped-to-go-values) nested struct field with `arg`, then including a positional argument field inside that struct _with the same name_. For example, the following command structure: - - app rename to - -Can be represented with the following: - -```go -var CLI struct { - Rename struct { - Name struct { - Name string `arg` // <-- NOTE: identical name to enclosing struct field. - To struct { - Name struct { - Name string `arg` - } `arg` - } `cmd` - } `arg` - } `cmd` -} -``` - -This looks a little verbose in this contrived example, but typically this will not be the case. - -## Positional arguments - -If a field is tagged with `arg:""` it will be treated as the final positional -value to be parsed on the command line. By default positional arguments are -required, but specifying `optional:""` will alter this. - -If a positional argument is a slice, all remaining arguments will be appended -to that slice. - -## Slices - -Slice values are treated specially. First the input is split on the `sep:""` tag (defaults to `,`), then each element is parsed by the slice element type and appended to the slice. If the same value is encountered multiple times, elements continue to be appended. - -To represent the following command-line: - - cmd ls ... - -You would use the following: - -```go -var CLI struct { - Ls struct { - Files []string `arg:"" type:"existingfile"` - } `cmd` -} -``` - -## Maps - -Maps are similar to slices except that only one key/value pair can be assigned per value, and the `sep` tag denotes the assignment character and defaults to `=`. - -To represent the following command-line: - - cmd config set = = ... - -You would use the following: - -```go -var CLI struct { - Config struct { - Set struct { - Config map[string]float64 `arg:"" type:"file:"` - } `cmd` - } `cmd` -} -``` - -For flags, multiple key+value pairs should be separated by `mapsep:"rune"` tag (defaults to `;`) eg. `--set="key1=value1;key2=value2"`. - -## Pointers - -Pointers work like the underlying type, except that you can differentiate between the presence of the zero value and no value being supplied. - -For example: - -```go -var CLI struct { - Foo *int -} -``` - -Would produce a nil value for `Foo` if no `--foo` argument is supplied, but would have a pointer to the value 0 if the argument `--foo=0` was supplied. - -## Nested data structure - -Kong support a nested data structure as well with `embed:""`. You can combine `embed:""` with `prefix:""`: - -```go -var CLI struct { - Logging struct { - Level string `enum:"debug,info,warn,error" default:"info"` - Type string `enum:"json,console" default:"console"` - } `embed:"" prefix:"logging."` -} -``` - -This configures Kong to accept flags `--logging.level` and `--logging.type`. - -## Custom named decoders - -Kong includes a number of builtin custom type mappers. These can be used by -specifying the tag `type:""`. They are registered with the option -function `NamedMapper(name, mapper)`. - -| Name | Description | -| -------------- | ---------------------------------------------------------------------------------------------------------------------- | -| `path` | A path. ~ expansion is applied. `-` is accepted for stdout, and will be passed unaltered. | -| `existingfile` | An existing file. ~ expansion is applied. `-` is accepted for stdin, and will be passed unaltered. | -| `existingdir` | An existing directory. ~ expansion is applied. | -| `counter` | Increment a numeric field. Useful for `-vvv`. Can accept `-s`, `--long` or `--long=N`. | -| `filecontent` | Read the file at path into the field. ~ expansion is applied. `-` is accepted for stdin, and will be passed unaltered. | - -Slices and maps treat type tags specially. For slices, the `type:""` tag -specifies the element type. For maps, the tag has the format -`tag:"[]:[]"` where either may be omitted. - -## Supported field types - -## Custom decoders (mappers) - -Any field implementing `encoding.TextUnmarshaler` or `json.Unmarshaler` will use those interfaces -for decoding values. Kong also includes builtin support for many common Go types: - -| Type | Description | -| --------------- | ----------------------------------------------------------------------------------------------------------- | -| `time.Duration` | Populated using `time.ParseDuration()`. | -| `time.Time` | Populated using `time.Parse()`. Format defaults to RFC3339 but can be overridden with the `format:"X"` tag. | -| `*os.File` | Path to a file that will be opened, or `-` for `os.Stdin`. File must be closed by the user. | -| `*url.URL` | Populated with `url.Parse()`. | - -For more fine-grained control, if a field implements the -[MapperValue](https://godoc.org/github.com/alecthomas/kong#MapperValue) -interface it will be used to decode arguments into the field. - -## Supported tags - -Tags can be in two forms: - -1. Standard Go syntax, eg. `kong:"required,name='foo'"`. -2. Bare tags, eg. `required:"" name:"foo"` - -Both can coexist with standard Tag parsing. - -| Tag | Description | -| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| `cmd:""` | If present, struct is a command. | -| `arg:""` | If present, field is an argument. Required by default. | -| `env:"X,Y,..."` | Specify envars to use for default value. The envs are resolved in the declared order. The first value found is used. | -| `name:"X"` | Long name, for overriding field name. | -| `help:"X"` | Help text. | -| `type:"X"` | Specify [named types](#custom-named-decoders) to use. | -| `placeholder:"X"` | Placeholder input, if flag. e.g. `` `placeholder:""` `` will show `--flag-name=` when displaying help. | -| `default:"X"` | Default value. | -| `default:"1"` | On a command, make it the default. | -| `default:"withargs"` | On a command, make it the default and allow args/flags from that command | -| `short:"X"` | Short name, if flag. | -| `aliases:"X,Y"` | One or more aliases (for cmd or flag). | -| `required:""` | If present, flag/arg is required. | -| `optional:""` | If present, flag/arg is optional. | -| `hidden:""` | If present, command or flag is hidden. | -| `negatable:""` | If present on a `bool` field, supports prefixing a flag with `--no-` to invert the default value | -| `negatable:"X"` | If present on a `bool` field, supports `--X` to invert the default value | -| `format:"X"` | Format for parsing input, if supported. | -| `sep:"X"` | Separator for sequences (defaults to ","). May be `none` to disable splitting. | -| `mapsep:"X"` | Separator for maps (defaults to ";"). May be `none` to disable splitting. | -| `enum:"X,Y,..."` | Set of valid values allowed for this flag. An enum field must be `required` or have a valid `default`. | -| `group:"X"` | Logical group for a flag or command. | -| `xor:"X,Y,..."` | Exclusive OR groups for flags. Only one flag in the group can be used which is restricted within the same command. When combined with `required`, at least one of the `xor` group will be required. | -| `and:"X,Y,..."` | AND groups for flags. All flags in the group must be used in the same command. When combined with `required`, all flags in the group will be required. | -| `prefix:"X"` | Prefix for all sub-flags. | -| `envprefix:"X"` | Envar prefix for all sub-flags. | -| `xorprefix:"X"` | Prefix for all sub-flags in XOR/AND groups. | -| `set:"K=V"` | Set a variable for expansion by child elements. Multiples can occur. | -| `embed:""` | If present, this field's children will be embedded in the parent. Useful for composition. | -| `passthrough:""`[^1] | If present on a positional argument, it stops flag parsing when encountered, as if `--` was processed before. Useful for external command wrappers, like `exec`. On a command it requires that the command contains only one argument of type `[]string` which is then filled with everything following the command, unparsed. | -| `-` | Ignore the field. Useful for adding non-CLI fields to a configuration struct. e.g `` `kong:"-"` `` | - -[^1]: `` can be `partial` or `all` (the default). `all` will pass through all arguments including flags, including -flags. `partial` will validate flags until the first positional argument is encountered, then pass through all remaining -positional arguments. - -## Plugins - -Kong CLI's can be extended by embedding the `kong.Plugin` type and populating it with pointers to Kong annotated structs. For example: - -```go -var pluginOne struct { - PluginOneFlag string -} -var pluginTwo struct { - PluginTwoFlag string -} -var cli struct { - BaseFlag string - kong.Plugins -} -cli.Plugins = kong.Plugins{&pluginOne, &pluginTwo} -``` - -Additionally if an interface type is embedded, it can also be populated with a Kong annotated struct. - -## Dynamic Commands - -While plugins give complete control over extending command-line interfaces, Kong -also supports dynamically adding commands via `kong.DynamicCommand()`. - -## Variable interpolation - -Kong supports limited variable interpolation into help strings, placeholder strings, -enum lists and default values. - -Variables are in the form: - - ${} - ${=} - -Variables are set with the `Vars{"key": "value", ...}` option. Undefined -variable references in the grammar without a default will result in an error at -construction time. - -Variables can also be set via the `set:"K=V"` tag. In this case, those variables will be available for that -node and all children. This is useful for composition by allowing the same struct to be reused. - -When interpolating into flag or argument help strings, some extra variables -are defined from the value itself: - - ${default} - ${enum} - -For flags with associated environment variables, the variable `${env}` can be -interpolated into the help string. In the absence of this variable in the -help string, Kong will append `($$${env})` to the help string. - -eg. - -```go -type cli struct { - Config string `type:"path" default:"${config_file}"` -} - -func main() { - kong.Parse(&cli, - kong.Vars{ - "config_file": "~/.app.conf", - }) -} -``` - -## Validation - -Kong does validation on the structure of a command-line, but also supports -extensible validation. Any node in the tree may implement either of the following interfaces: - -```go -type Validatable interface { - Validate() error - } -``` - -```go -type Validatable interface { - Validate(kctx *kong.Context) error - } -``` - -If one of these nodes is in the active command-line it will be called during -normal validation. - -## Modifying Kong's behaviour - -Each Kong parser can be configured via functional options passed to `New(cli any, options...Option)`. - -The full set of options can be found [here](https://godoc.org/github.com/alecthomas/kong#Option). - -### `Name(help)` and `Description(help)` - set the application name description - -Set the application name and/or description. - -The name of the application will default to the binary name, but can be overridden with `Name(name)`. - -As with all help in Kong, text will be wrapped to the terminal. - -### `Configuration(loader, paths...)` - load defaults from configuration files - -This option provides Kong with support for loading defaults from a set of configuration files. Each file is opened, if possible, and the loader called to create a resolver for that file. - -eg. - -```go -kong.Parse(&cli, kong.Configuration(kong.JSON, "/etc/myapp.json", "~/.myapp.json")) -``` - -[See the tests](https://github.com/alecthomas/kong/blob/master/resolver_test.go#L206) for an example of how the JSON file is structured. - -#### List of Configuration Loaders - -- [YAML](https://github.com/alecthomas/kong-yaml) -- [HCL](https://github.com/alecthomas/kong-hcl) -- [TOML](https://github.com/alecthomas/kong-toml) -- [JSON](https://github.com/alecthomas/kong) - -### `Resolver(...)` - support for default values from external sources - -Resolvers are Kong's extension point for providing default values from external sources. As an example, support for environment variables via the `env` tag is provided by a resolver. There's also a builtin resolver for JSON configuration files. - -Example resolvers can be found in [resolver.go](https://github.com/alecthomas/kong/blob/master/resolver.go). - -### `*Mapper(...)` - customising how the command-line is mapped to Go values - -Command-line arguments are mapped to Go values via the Mapper interface: - -```go -// A Mapper represents how a field is mapped from command-line values to Go. -// -// Mappers can be associated with concrete fields via pointer, reflect.Type, reflect.Kind, or via a "type" tag. -// -// Additionally, if a type implements the MapperValue interface, it will be used. -type Mapper interface { - // Decode ctx.Value with ctx.Scanner into target. - Decode(ctx *DecodeContext, target reflect.Value) error -} -``` - -All builtin Go types (as well as a bunch of useful stdlib types like `time.Time`) have mappers registered by default. Mappers for custom types can be added using `kong.??Mapper(...)` options. Mappers are applied to fields in four ways: - -1. `NamedMapper(string, Mapper)` and using the tag key `type:""`. -2. `KindMapper(reflect.Kind, Mapper)`. -3. `TypeMapper(reflect.Type, Mapper)`. -4. `ValueMapper(any, Mapper)`, passing in a pointer to a field of the grammar. - -### `ConfigureHelp(HelpOptions)` and `Help(HelpFunc)` - customising help - -The default help output is usually sufficient, but if not there are two solutions. - -1. Use `ConfigureHelp(HelpOptions)` to configure how help is formatted (see [HelpOptions](https://godoc.org/github.com/alecthomas/kong#HelpOptions) for details). -2. Custom help can be wired into Kong via the `Help(HelpFunc)` option. The `HelpFunc` is passed a `Context`, which contains the parsed context for the current command-line. See the implementation of `DefaultHelpPrinter` for an example. -3. Use `ValueFormatter(HelpValueFormatter)` if you want to just customize the help text that is accompanied by flags and arguments. -4. Use `Groups([]Group)` if you want to customize group titles or add a header. - -### Injecting values into `Run()` methods - -There are several ways to inject values into `Run()` methods: - -1. Use `Bind()` to bind values directly. -2. Use `BindTo()` to bind values to an interface type. -3. Use `BindToProvider()` to bind values to a function that provides the value. -4. Implement `Provide() error` methods on the command structure. - -### Other options - -The full set of options can be found [here](https://godoc.org/github.com/alecthomas/kong#Option). diff --git a/vendor/github.com/alecthomas/kong/build.go b/vendor/github.com/alecthomas/kong/build.go deleted file mode 100644 index 63afcd4..0000000 --- a/vendor/github.com/alecthomas/kong/build.go +++ /dev/null @@ -1,411 +0,0 @@ -package kong - -import ( - "fmt" - "reflect" - "strings" -) - -// Plugins are dynamically embedded command-line structures. -// -// Each element in the Plugins list *must* be a pointer to a structure. -type Plugins []any - -func build(k *Kong, ast any) (app *Application, err error) { - v := reflect.ValueOf(ast) - iv := reflect.Indirect(v) - if v.Kind() != reflect.Ptr || iv.Kind() != reflect.Struct { - return nil, fmt.Errorf("expected a pointer to a struct but got %T", ast) - } - - app = &Application{} - extraFlags := k.extraFlags() - seenFlags := map[string]bool{} - for _, flag := range extraFlags { - seenFlags[flag.Name] = true - } - - node, err := buildNode(k, iv, ApplicationNode, newEmptyTag(), seenFlags) - if err != nil { - return nil, err - } - if len(node.Positional) > 0 && len(node.Children) > 0 { - return nil, fmt.Errorf("can't mix positional arguments and branching arguments on %T", ast) - } - app.Node = node - app.Node.Flags = append(extraFlags, app.Node.Flags...) - app.Tag = newEmptyTag() - app.Tag.Vars = k.vars - return app, nil -} - -func dashedString(s string) string { - return strings.Join(camelCase(s), "-") -} - -type flattenedField struct { - field reflect.StructField - value reflect.Value - tag *Tag -} - -func flattenedFields(v reflect.Value, ptag *Tag) (out []flattenedField, err error) { - v = reflect.Indirect(v) - if v.Kind() != reflect.Struct { - return out, nil - } - ignored := map[string]bool{} - for i := 0; i < v.NumField(); i++ { - ft := v.Type().Field(i) - fv := v.Field(i) - tag, err := parseTag(v, ft) - if err != nil { - return nil, err - } - if tag.Ignored || ignored[ft.Name] { - ignored[ft.Name] = true - continue - } - // Assign group if it's not already set. - if tag.Group == "" { - tag.Group = ptag.Group - } - // Accumulate prefixes. - tag.Prefix = ptag.Prefix + tag.Prefix - tag.EnvPrefix = ptag.EnvPrefix + tag.EnvPrefix - tag.XorPrefix = ptag.XorPrefix + tag.XorPrefix - // Combine parent vars. - tag.Vars = ptag.Vars.CloneWith(tag.Vars) - // Command and embedded structs can be pointers, so we hydrate them now. - if (tag.Cmd || tag.Embed) && ft.Type.Kind() == reflect.Ptr { - fv = reflect.New(ft.Type.Elem()).Elem() - v.FieldByIndex(ft.Index).Set(fv.Addr()) - } - if !ft.Anonymous && !tag.Embed { - if fv.CanSet() { - field := flattenedField{field: ft, value: fv, tag: tag} - out = append(out, field) - } - continue - } - - // Embedded type. - if fv.Kind() == reflect.Interface { - fv = fv.Elem() - } else if fv.Type() == reflect.TypeOf(Plugins{}) { - for i := 0; i < fv.Len(); i++ { - fields, ferr := flattenedFields(fv.Index(i).Elem(), tag) - if ferr != nil { - return nil, ferr - } - out = append(out, fields...) - } - continue - } - sub, err := flattenedFields(fv, tag) - if err != nil { - return nil, err - } - out = append(out, sub...) - } - out = removeIgnored(out, ignored) - return out, nil -} - -func removeIgnored(fields []flattenedField, ignored map[string]bool) []flattenedField { - j := 0 - for i := 0; i < len(fields); i++ { - if ignored[fields[i].field.Name] { - continue - } - if i != j { - fields[j] = fields[i] - } - j++ - } - if j != len(fields) { - fields = fields[:j] - } - return fields -} - -// Build a Node in the Kong data model. -// -// "v" is the value to create the node from, "typ" is the output Node type. -func buildNode(k *Kong, v reflect.Value, typ NodeType, tag *Tag, seenFlags map[string]bool) (*Node, error) { //nolint:gocyclo - node := &Node{ - Type: typ, - Target: v, - Tag: tag, - } - fields, err := flattenedFields(v, tag) - if err != nil { - return nil, err - } - -MAIN: - for _, field := range fields { - for _, r := range k.ignoreFields { - if r.MatchString(v.Type().Name() + "." + field.field.Name) { - continue MAIN - } - } - - ft := field.field - fv := field.value - - tag := field.tag - name := tag.Name - if name == "" { - name = tag.Prefix + k.flagNamer(ft.Name) - } else { - name = tag.Prefix + name - } - - if len(tag.Envs) != 0 { - for i := range tag.Envs { - tag.Envs[i] = tag.EnvPrefix + tag.Envs[i] - } - } - - if len(tag.Xor) != 0 { - for i := range tag.Xor { - tag.Xor[i] = tag.XorPrefix + tag.Xor[i] - } - } - - if len(tag.And) != 0 { - for i := range tag.And { - tag.And[i] = tag.XorPrefix + tag.And[i] - } - } - - // Nested structs are either commands or args, unless they implement the Mapper interface. - if field.value.Kind() == reflect.Struct && (tag.Cmd || tag.Arg) && k.registry.ForValue(fv) == nil { - typ := CommandNode - if tag.Arg { - typ = ArgumentNode - } - err = buildChild(k, node, typ, v, ft, fv, tag, name, seenFlags) - } else { - err = buildField(k, node, v, ft, fv, tag, name, seenFlags) - } - if err != nil { - return nil, err - } - } - - // Validate if there are no duplicate names - if err := checkDuplicateNames(node, v); err != nil { - return nil, err - } - - // "Unsee" flags. - for _, flag := range node.Flags { - delete(seenFlags, "--"+flag.Name) - if flag.Short != 0 { - delete(seenFlags, "-"+string(flag.Short)) - } - if negFlag := negatableFlagName(flag.Name, flag.Tag.Negatable); negFlag != "" { - delete(seenFlags, negFlag) - } - for _, aflag := range flag.Aliases { - delete(seenFlags, "--"+aflag) - } - } - - if err := validatePositionalArguments(node); err != nil { - return nil, err - } - - return node, nil -} - -func validatePositionalArguments(node *Node) error { - var last *Value - for i, curr := range node.Positional { - if last != nil { - // Scan through argument positionals to ensure optional is never before a required. - if !last.Required && curr.Required { - return fmt.Errorf("%s: required %q cannot come after optional %q", node.FullPath(), curr.Name, last.Name) - } - - // Cumulative argument needs to be last. - if last.IsCumulative() { - return fmt.Errorf("%s: argument %q cannot come after cumulative %q", node.FullPath(), curr.Name, last.Name) - } - } - - last = curr - curr.Position = i - } - - return nil -} - -func buildChild(k *Kong, node *Node, typ NodeType, v reflect.Value, ft reflect.StructField, fv reflect.Value, tag *Tag, name string, seenFlags map[string]bool) error { - child, err := buildNode(k, fv, typ, newEmptyTag(), seenFlags) - if err != nil { - return err - } - child.Name = name - child.Tag = tag - child.Parent = node - child.Help = tag.Help - child.Hidden = tag.Hidden - child.Group = buildGroupForKey(k, tag.Group) - child.Aliases = tag.Aliases - - if provider, ok := fv.Addr().Interface().(HelpProvider); ok { - child.Detail = provider.Help() - } - - // A branching argument. This is a bit hairy, as we let buildNode() do the parsing, then check that - // a positional argument is provided to the child, and move it to the branching argument field. - if tag.Arg { - if len(child.Positional) == 0 { - return failField(v, ft, "positional branch must have at least one child positional argument named %q", name) - } - if child.Positional[0].Name != name { - return failField(v, ft, "first field in positional branch must have the same name as the parent field (%s).", child.Name) - } - - child.Argument = child.Positional[0] - child.Positional = child.Positional[1:] - if child.Help == "" { - child.Help = child.Argument.Help - } - } else { - if tag.HasDefault { - if node.DefaultCmd != nil { - return failField(v, ft, "can't have more than one default command under %s", node.Summary()) - } - if tag.Default != "withargs" && (len(child.Children) > 0 || len(child.Positional) > 0) { - return failField(v, ft, "default command %s must not have subcommands or arguments", child.Summary()) - } - node.DefaultCmd = child - } - if tag.Passthrough { - if len(child.Children) > 0 || len(child.Flags) > 0 { - return failField(v, ft, "passthrough command %s must not have subcommands or flags", child.Summary()) - } - if len(child.Positional) != 1 { - return failField(v, ft, "passthrough command %s must contain exactly one positional argument", child.Summary()) - } - if !checkPassthroughArg(child.Positional[0].Target) { - return failField(v, ft, "passthrough command %s must contain exactly one positional argument of []string type", child.Summary()) - } - child.Passthrough = true - } - } - node.Children = append(node.Children, child) - - if len(child.Positional) > 0 && len(child.Children) > 0 { - return failField(v, ft, "can't mix positional arguments and branching arguments") - } - - return nil -} - -func buildField(k *Kong, node *Node, v reflect.Value, ft reflect.StructField, fv reflect.Value, tag *Tag, name string, seenFlags map[string]bool) error { - mapper := k.registry.ForNamedValue(tag.Type, fv) - if mapper == nil { - return failField(v, ft, "unsupported field type %s, perhaps missing a cmd:\"\" tag?", ft.Type) - } - - value := &Value{ - Name: name, - Help: tag.Help, - OrigHelp: tag.Help, - HasDefault: tag.HasDefault, - Default: tag.Default, - DefaultValue: reflect.New(fv.Type()).Elem(), - Mapper: mapper, - Tag: tag, - Target: fv, - Enum: tag.Enum, - Passthrough: tag.Passthrough, - PassthroughMode: tag.PassthroughMode, - - // Flags are optional by default, and args are required by default. - Required: (!tag.Arg && tag.Required) || (tag.Arg && !tag.Optional), - Format: tag.Format, - } - - if tag.Arg { - node.Positional = append(node.Positional, value) - } else { - if seenFlags["--"+value.Name] { - return failField(v, ft, "duplicate flag --%s", value.Name) - } - seenFlags["--"+value.Name] = true - for _, alias := range tag.Aliases { - aliasFlag := "--" + alias - if seenFlags[aliasFlag] { - return failField(v, ft, "duplicate flag %s", aliasFlag) - } - seenFlags[aliasFlag] = true - } - if tag.Short != 0 { - if seenFlags["-"+string(tag.Short)] { - return failField(v, ft, "duplicate short flag -%c", tag.Short) - } - seenFlags["-"+string(tag.Short)] = true - } - if tag.Negatable != "" { - negFlag := negatableFlagName(value.Name, tag.Negatable) - if seenFlags[negFlag] { - return failField(v, ft, "duplicate negation flag %s", negFlag) - } - seenFlags[negFlag] = true - } - flag := &Flag{ - Value: value, - Aliases: tag.Aliases, - Short: tag.Short, - PlaceHolder: tag.PlaceHolder, - Envs: tag.Envs, - Group: buildGroupForKey(k, tag.Group), - Xor: tag.Xor, - And: tag.And, - Hidden: tag.Hidden, - } - value.Flag = flag - node.Flags = append(node.Flags, flag) - } - return nil -} - -func buildGroupForKey(k *Kong, key string) *Group { - if key == "" { - return nil - } - for _, group := range k.groups { - if group.Key == key { - return &group - } - } - - // No group provided with kong.ExplicitGroups. We create one ad-hoc for this key. - return &Group{ - Key: key, - Title: key, - } -} - -func checkDuplicateNames(node *Node, v reflect.Value) error { - seenNames := make(map[string]struct{}) - for _, node := range node.Children { - if _, ok := seenNames[node.Name]; ok { - name := v.Type().Name() - if name == "" { - name = "" - } - return fmt.Errorf("duplicate command name %q in command %q", node.Name, name) - } - - seenNames[node.Name] = struct{}{} - } - - return nil -} diff --git a/vendor/github.com/alecthomas/kong/callbacks.go b/vendor/github.com/alecthomas/kong/callbacks.go deleted file mode 100644 index 6096a26..0000000 --- a/vendor/github.com/alecthomas/kong/callbacks.go +++ /dev/null @@ -1,231 +0,0 @@ -package kong - -import ( - "fmt" - "reflect" - "strings" -) - -// binding is a single binding registered with Kong. -type binding struct { - // fn is a function that returns a value of the target type. - fn reflect.Value - - // val is a value of the target type. - // Must be set if done and singleton are true. - val reflect.Value - - // singleton indicates whether the binding is a singleton. - // If true, the binding will be resolved once and cached. - singleton bool - - // done indicates whether a singleton binding has been resolved. - // If singleton is false, this field is ignored. - done bool -} - -// newValueBinding builds a binding with an already resolved value. -func newValueBinding(v reflect.Value) *binding { - return &binding{val: v, done: true, singleton: true} -} - -// newFunctionBinding builds a binding with a function -// that will return a value of the target type. -// -// The function signature must be func(...) (T, error) or func(...) T -// where parameters are recursively resolved. -func newFunctionBinding(f reflect.Value, singleton bool) *binding { - return &binding{fn: f, singleton: singleton} -} - -// Get returns the pre-resolved value for the binding, -// or false if the binding is not resolved. -func (b *binding) Get() (v reflect.Value, ok bool) { - return b.val, b.done -} - -// Set sets the value of the binding to the given value, -// marking it as resolved. -// -// If the binding is not a singleton, this method does nothing. -func (b *binding) Set(v reflect.Value) { - if b.singleton { - b.val = v - b.done = true - } -} - -// A map of type to function that returns a value of that type. -// -// The function should have the signature func(...) (T, error). Arguments are recursively resolved. -type bindings map[reflect.Type]*binding - -func (b bindings) String() string { - out := []string{} - for k := range b { - out = append(out, k.String()) - } - return "bindings{" + strings.Join(out, ", ") + "}" -} - -func (b bindings) add(values ...any) bindings { - for _, v := range values { - val := reflect.ValueOf(v) - b[val.Type()] = newValueBinding(val) - } - return b -} - -func (b bindings) addTo(impl, iface any) { - val := reflect.ValueOf(impl) - b[reflect.TypeOf(iface).Elem()] = newValueBinding(val) -} - -func (b bindings) addProvider(provider any, singleton bool) error { - pv := reflect.ValueOf(provider) - t := pv.Type() - if t.Kind() != reflect.Func { - return fmt.Errorf("%T must be a function", provider) - } - - if t.NumOut() == 0 { - return fmt.Errorf("%T must be a function with the signature func(...)(T, error) or func(...) T", provider) - } - if t.NumOut() == 2 { - if t.Out(1) != reflect.TypeOf((*error)(nil)).Elem() { - return fmt.Errorf("missing error; %T must be a function with the signature func(...)(T, error) or func(...) T", provider) - } - } - rt := pv.Type().Out(0) - b[rt] = newFunctionBinding(pv, singleton) - return nil -} - -// Clone and add values. -func (b bindings) clone() bindings { - out := make(bindings, len(b)) - for k, v := range b { - out[k] = v - } - return out -} - -func (b bindings) merge(other bindings) bindings { - for k, v := range other { - b[k] = v - } - return b -} - -func getMethod(value reflect.Value, name string) reflect.Value { - method := value.MethodByName(name) - if !method.IsValid() { - if value.CanAddr() { - method = value.Addr().MethodByName(name) - } - } - return method -} - -// getMethods gets all methods with the given name from the given value -// and any embedded fields. -// -// Returns a slice of bound methods that can be called directly. -func getMethods(value reflect.Value, name string) (methods []reflect.Value) { - if value.Kind() == reflect.Ptr { - value = value.Elem() - } - if !value.IsValid() { - return - } - - if method := getMethod(value, name); method.IsValid() { - methods = append(methods, method) - } - - if value.Kind() != reflect.Struct { - return - } - // If the current value is a struct, also consider embedded fields. - // Two kinds of embedded fields are considered if they're exported: - // - // - standard Go embedded fields - // - fields tagged with `embed:""` - t := value.Type() - for i := 0; i < value.NumField(); i++ { - fieldValue := value.Field(i) - field := t.Field(i) - - if !field.IsExported() { - continue - } - - // Consider a field embedded if it's actually embedded - // or if it's tagged with `embed:""`. - _, isEmbedded := field.Tag.Lookup("embed") - isEmbedded = isEmbedded || field.Anonymous - if isEmbedded { - methods = append(methods, getMethods(fieldValue, name)...) - } - } - return -} - -func callFunction(f reflect.Value, bindings bindings) error { - if f.Kind() != reflect.Func { - return fmt.Errorf("expected function, got %s", f.Type()) - } - t := f.Type() - if t.NumOut() != 1 || !t.Out(0).Implements(callbackReturnSignature) { - return fmt.Errorf("return value of %s must implement \"error\"", t) - } - out, err := callAnyFunction(f, bindings) - if err != nil { - return err - } - ferr := out[0] - if ferrv := reflect.ValueOf(ferr); !ferrv.IsValid() || ((ferrv.Kind() == reflect.Interface || ferrv.Kind() == reflect.Pointer) && ferrv.IsNil()) { - return nil - } - return ferr.(error) //nolint:forcetypeassert -} - -func callAnyFunction(f reflect.Value, bindings bindings) (out []any, err error) { - if f.Kind() != reflect.Func { - return nil, fmt.Errorf("expected function, got %s", f.Type()) - } - in := []reflect.Value{} - t := f.Type() - for i := 0; i < t.NumIn(); i++ { - pt := t.In(i) - binding, ok := bindings[pt] - if !ok { - return nil, fmt.Errorf("couldn't find binding of type %s for parameter %d of %s(), use kong.Bind(%s)", pt, i, t, pt) - } - - // Don't need to call the function if the value is already resolved. - if val, ok := binding.Get(); ok { - in = append(in, val) - continue - } - - // Recursively resolve binding functions. - argv, err := callAnyFunction(binding.fn, bindings) - if err != nil { - return nil, fmt.Errorf("%s: %w", pt, err) - } - if ferrv := reflect.ValueOf(argv[len(argv)-1]); ferrv.IsValid() && ferrv.Type().Implements(callbackReturnSignature) && !ferrv.IsNil() { - return nil, ferrv.Interface().(error) //nolint:forcetypeassert - } - - val := reflect.ValueOf(argv[0]) - binding.Set(val) - in = append(in, val) - } - outv := f.Call(in) - out = make([]any, len(outv)) - for i, v := range outv { - out[i] = v.Interface() - } - return out, nil -} diff --git a/vendor/github.com/alecthomas/kong/camelcase.go b/vendor/github.com/alecthomas/kong/camelcase.go deleted file mode 100644 index a955b16..0000000 --- a/vendor/github.com/alecthomas/kong/camelcase.go +++ /dev/null @@ -1,90 +0,0 @@ -package kong - -// NOTE: This code is from https://github.com/fatih/camelcase. MIT license. - -import ( - "unicode" - "unicode/utf8" -) - -// Split splits the camelcase word and returns a list of words. It also -// supports digits. Both lower camel case and upper camel case are supported. -// For more info please check: http://en.wikipedia.org/wiki/CamelCase -// -// Examples -// -// "" => [""] -// "lowercase" => ["lowercase"] -// "Class" => ["Class"] -// "MyClass" => ["My", "Class"] -// "MyC" => ["My", "C"] -// "HTML" => ["HTML"] -// "PDFLoader" => ["PDF", "Loader"] -// "AString" => ["A", "String"] -// "SimpleXMLParser" => ["Simple", "XML", "Parser"] -// "vimRPCPlugin" => ["vim", "RPC", "Plugin"] -// "GL11Version" => ["GL", "11", "Version"] -// "99Bottles" => ["99", "Bottles"] -// "May5" => ["May", "5"] -// "BFG9000" => ["BFG", "9000"] -// "BöseÜberraschung" => ["Böse", "Überraschung"] -// "Two spaces" => ["Two", " ", "spaces"] -// "BadUTF8\xe2\xe2\xa1" => ["BadUTF8\xe2\xe2\xa1"] -// -// Splitting rules -// -// 1. If string is not valid UTF-8, return it without splitting as -// single item array. -// 2. Assign all unicode characters into one of 4 sets: lower case -// letters, upper case letters, numbers, and all other characters. -// 3. Iterate through characters of string, introducing splits -// between adjacent characters that belong to different sets. -// 4. Iterate through array of split strings, and if a given string -// is upper case: -// if subsequent string is lower case: -// move last character of upper case string to beginning of -// lower case string -func camelCase(src string) (entries []string) { - // don't split invalid utf8 - if !utf8.ValidString(src) { - return []string{src} - } - entries = []string{} - var runes [][]rune - lastClass := 0 - // split into fields based on class of unicode character - for _, r := range src { - var class int - switch { - case unicode.IsLower(r): - class = 1 - case unicode.IsUpper(r): - class = 2 - case unicode.IsDigit(r): - class = 3 - default: - class = 4 - } - if class == lastClass { - runes[len(runes)-1] = append(runes[len(runes)-1], r) - } else { - runes = append(runes, []rune{r}) - } - lastClass = class - } - // handle upper case -> lower case sequences, e.g. - // "PDFL", "oader" -> "PDF", "Loader" - for i := 0; i < len(runes)-1; i++ { - if unicode.IsUpper(runes[i][0]) && unicode.IsLower(runes[i+1][0]) { - runes[i+1] = append([]rune{runes[i][len(runes[i])-1]}, runes[i+1]...) - runes[i] = runes[i][:len(runes[i])-1] - } - } - // construct []string from results - for _, s := range runes { - if len(s) > 0 { - entries = append(entries, string(s)) - } - } - return entries -} diff --git a/vendor/github.com/alecthomas/kong/context.go b/vendor/github.com/alecthomas/kong/context.go deleted file mode 100644 index 6a4989f..0000000 --- a/vendor/github.com/alecthomas/kong/context.go +++ /dev/null @@ -1,1183 +0,0 @@ -package kong - -import ( - "errors" - "fmt" - "os" - "reflect" - "sort" - "strconv" - "strings" -) - -// Path records the nodes and parsed values from the current command-line. -type Path struct { - Parent *Node - - // One of these will be non-nil. - App *Application - Positional *Positional - Flag *Flag - Argument *Argument - Command *Command - - // Flags added by this node. - Flags []*Flag - - // True if this Path element was created as the result of a resolver. - Resolved bool - - // Remaining tokens after this node - remainder []Token -} - -// Node returns the Node associated with this Path, or nil if Path is a non-Node. -func (p *Path) Node() *Node { - switch { - case p.App != nil: - return p.App.Node - - case p.Argument != nil: - return p.Argument - - case p.Command != nil: - return p.Command - } - return nil -} - -// Visitable returns the Visitable for this path element. -func (p *Path) Visitable() Visitable { - switch { - case p.App != nil: - return p.App - - case p.Argument != nil: - return p.Argument - - case p.Command != nil: - return p.Command - - case p.Flag != nil: - return p.Flag - - case p.Positional != nil: - return p.Positional - } - return nil -} - -// Remainder returns the remaining unparsed args after this Path element. -func (p *Path) Remainder() []string { - args := []string{} - for _, token := range p.remainder { - args = append(args, token.String()) - } - return args -} - -// Context contains the current parse context. -type Context struct { - *Kong - // A trace through parsed nodes. - Path []*Path - // Original command-line arguments. - Args []string - // Error that occurred during trace, if any. - Error error - - values map[*Value]reflect.Value // Temporary values during tracing. - bindings bindings - resolvers []Resolver // Extra context-specific resolvers. - scan *Scanner -} - -// Trace path of "args" through the grammar tree. -// -// The returned Context will include a Path of all commands, arguments, positionals and flags. -// -// This just constructs a new trace. To fully apply the trace you must call Reset(), Resolve(), -// Validate() and Apply(). -func Trace(k *Kong, args []string) (*Context, error) { - s := Scan(args...).AllowHyphenPrefixedParameters(k.allowHyphenated) - c := &Context{ - Kong: k, - Args: args, - Path: []*Path{ - {App: k.Model, Flags: k.Model.Flags, remainder: s.PeekAll()}, - }, - values: map[*Value]reflect.Value{}, - scan: s, - bindings: bindings{}, - } - c.Error = c.trace(c.Model.Node) - return c, nil -} - -// Bind adds bindings to the Context. -func (c *Context) Bind(args ...any) { - c.bindings.add(args...) -} - -// BindTo adds a binding to the Context. -// -// This will typically have to be called like so: -// -// BindTo(impl, (*MyInterface)(nil)) -func (c *Context) BindTo(impl, iface any) { - c.bindings.addTo(impl, iface) -} - -// BindToProvider allows binding of provider functions. -// -// This is useful when the Run() function of different commands require different values that may -// not all be initialisable from the main() function. -// -// "provider" must be a function with the signature func(...) (T, error) or func(...) T, -// where ... will be recursively injected with bound values. -func (c *Context) BindToProvider(provider any) error { - return c.bindings.addProvider(provider, false /* singleton */) -} - -// BindSingletonProvider allows binding of provider functions. -// The provider will be called once and the result cached. -// -// "provider" must be a function with the signature func(...) (T, error) or func(...) T, -// where ... will be recursively injected with bound values. -func (c *Context) BindSingletonProvider(provider any) error { - return c.bindings.addProvider(provider, true /* singleton */) -} - -// Value returns the value for a particular path element. -func (c *Context) Value(path *Path) reflect.Value { - switch { - case path.Positional != nil: - return c.values[path.Positional] - case path.Flag != nil: - return c.values[path.Flag.Value] - case path.Argument != nil: - return c.values[path.Argument.Argument] - } - panic("can only retrieve value for flag, argument or positional") -} - -// Selected command or argument. -func (c *Context) Selected() *Node { - var selected *Node - for _, path := range c.Path { - switch { - case path.Command != nil: - selected = path.Command - case path.Argument != nil: - selected = path.Argument - } - } - return selected -} - -// Empty returns true if there were no arguments provided. -func (c *Context) Empty() bool { - for _, path := range c.Path { - if !path.Resolved && path.App == nil { - return false - } - } - return true -} - -// Validate the current context. -func (c *Context) Validate() error { //nolint: gocyclo - err := Visit(c.Model, func(node Visitable, next Next) error { - switch node := node.(type) { - case *Value: - ok := atLeastOneEnvSet(node.Tag.Envs) - if node.Enum != "" && (!node.Required || node.HasDefault || (len(node.Tag.Envs) != 0 && ok)) { - if err := checkEnum(node, node.Target); err != nil { - return err - } - } - - case *Flag: - ok := atLeastOneEnvSet(node.Tag.Envs) - if node.Enum != "" && (!node.Required || node.HasDefault || (len(node.Tag.Envs) != 0 && ok)) { - if err := checkEnum(node.Value, node.Target); err != nil { - return err - } - } - } - return next(nil) - }) - if err != nil { - return err - } - for _, el := range c.Path { - var ( - value reflect.Value - desc string - ) - switch node := el.Visitable().(type) { - case *Value: - value = node.Target - desc = node.ShortSummary() - - case *Flag: - value = node.Target - desc = node.ShortSummary() - - case *Application: - value = node.Target - desc = "" - - case *Node: - value = node.Target - desc = node.Path() - } - if validate := isValidatable(value); validate != nil { - if err := validate.Validate(c); err != nil { - if desc != "" { - return fmt.Errorf("%s: %w", desc, err) - } - return err - } - } - } - for _, resolver := range c.combineResolvers() { - if err := resolver.Validate(c.Model); err != nil { - return err - } - } - for _, path := range c.Path { - var value *Value - switch { - case path.Flag != nil: - value = path.Flag.Value - - case path.Positional != nil: - value = path.Positional - } - if value != nil && value.Tag.Enum != "" { - if err := checkEnum(value, value.Target); err != nil { - return err - } - } - if err := checkMissingFlags(path.Flags); err != nil { - return err - } - } - // Check the terminal node. - node := c.Selected() - if node == nil { - node = c.Model.Node - } - - // Find deepest positional argument so we can check if all required positionals have been provided. - positionals := 0 - for _, path := range c.Path { - if path.Positional != nil { - positionals = path.Positional.Position + 1 - } - } - - if err := checkMissingChildren(node); err != nil { - return err - } - if err := checkMissingPositionals(positionals, node.Positional); err != nil { - return err - } - if err := checkXorDuplicatedAndAndMissing(c.Path); err != nil { - return err - } - - if node.Type == ArgumentNode { - value := node.Argument - if value.Required && !value.Set { - return fmt.Errorf("%s is required", node.Summary()) - } - } - return nil -} - -// Flags returns the accumulated available flags. -func (c *Context) Flags() (flags []*Flag) { - for _, trace := range c.Path { - flags = append(flags, trace.Flags...) - } - return -} - -// Command returns the full command path. -func (c *Context) Command() string { - command := []string{} - for _, trace := range c.Path { - switch { - case trace.Positional != nil: - command = append(command, "<"+trace.Positional.Name+">") - - case trace.Argument != nil: - command = append(command, "<"+trace.Argument.Name+">") - - case trace.Command != nil: - command = append(command, trace.Command.Name) - } - } - return strings.Join(command, " ") -} - -// AddResolver adds a context-specific resolver. -// -// This is most useful in the BeforeResolve() hook. -func (c *Context) AddResolver(resolver Resolver) { - c.resolvers = append(c.resolvers, resolver) -} - -// FlagValue returns the set value of a flag if it was encountered and exists, or its default value. -func (c *Context) FlagValue(flag *Flag) any { - for _, trace := range c.Path { - if trace.Flag == flag { - v, ok := c.values[trace.Flag.Value] - if !ok { - break - } - return v.Interface() - } - } - if flag.Target.IsValid() { - return flag.Target.Interface() - } - return flag.DefaultValue.Interface() -} - -// Reset recursively resets values to defaults (as specified in the grammar) or the zero value. -func (c *Context) Reset() error { - return Visit(c.Model.Node, func(node Visitable, next Next) error { - if value, ok := node.(*Value); ok { - return next(value.Reset()) - } - return next(nil) - }) -} - -func (c *Context) endParsing() { - args := []string{} - for { - token := c.scan.Pop() - if token.Type == EOLToken { - break - } - args = append(args, token.String()) - } - // Note: tokens must be pushed in reverse order. - for i := range args { - c.scan.PushTyped(args[len(args)-1-i], PositionalArgumentToken) - } -} - -//nolint:maintidx -func (c *Context) trace(node *Node) (err error) { //nolint: gocyclo - positional := 0 - node.Active = true - - flags := []*Flag{} - flagNode := node - if node.DefaultCmd != nil && node.DefaultCmd.Tag.Default == "withargs" { - // Add flags of the default command if the current node has one - // and that default command allows args / flags without explicitly - // naming the command on the CLI. - flagNode = node.DefaultCmd - } - for _, group := range flagNode.AllFlags(false) { - flags = append(flags, group...) - } - - if node.Passthrough { - c.endParsing() - } - - for !c.scan.Peek().IsEOL() { - token := c.scan.Peek() - switch token.Type { - case UntypedToken: - switch v := token.Value.(type) { - case string: - - switch { - case v == "-": - fallthrough - default: //nolint - c.scan.Pop() - c.scan.PushTyped(token.Value, PositionalArgumentToken) - - // Indicates end of parsing. All remaining arguments are treated as positional arguments only. - case v == "--": - c.endParsing() - - // Pop the -- token unless the next positional argument accepts passthrough arguments. - if !(positional < len(node.Positional) && node.Positional[positional].Passthrough) { - c.scan.Pop() - } - - // Long flag. - case strings.HasPrefix(v, "--"): - c.scan.Pop() - // Parse it and push the tokens. - parts := strings.SplitN(v[2:], "=", 2) - if len(parts) > 1 { - c.scan.PushTyped(parts[1], FlagValueToken) - } - c.scan.PushTyped(parts[0], FlagToken) - - // Short flag. - case strings.HasPrefix(v, "-"): - c.scan.Pop() - // Note: tokens must be pushed in reverse order. - if tail := v[2:]; tail != "" { - c.scan.PushTyped(tail, ShortFlagTailToken) - } - c.scan.PushTyped(v[1:2], ShortFlagToken) - } - default: - c.scan.Pop() - c.scan.PushTyped(token.Value, PositionalArgumentToken) - } - - case ShortFlagTailToken: - c.scan.Pop() - // Note: tokens must be pushed in reverse order. - if tail := token.String()[1:]; tail != "" { - c.scan.PushTyped(tail, ShortFlagTailToken) - } - c.scan.PushTyped(token.String()[0:1], ShortFlagToken) - - case FlagToken: - if err := c.parseFlag(flags, token.String()); err != nil { - if isUnknownFlagError(err) && positional < len(node.Positional) && node.Positional[positional].PassthroughMode == PassThroughModeAll { - c.scan.Pop() - c.scan.PushTyped(token.String(), PositionalArgumentToken) - } else { - return err - } - } - - case ShortFlagToken: - if err := c.parseFlag(flags, token.String()); err != nil { - if isUnknownFlagError(err) && positional < len(node.Positional) && node.Positional[positional].PassthroughMode == PassThroughModeAll { - c.scan.Pop() - c.scan.PushTyped(token.String(), PositionalArgumentToken) - } else { - return err - } - } - - case FlagValueToken: - return fmt.Errorf("unexpected flag argument %q", token.Value) - - case PositionalArgumentToken: - candidates := []string{} - - // Ensure we've consumed all positional arguments. - if positional < len(node.Positional) { - arg := node.Positional[positional] - - if arg.Passthrough { - c.endParsing() - } - - arg.Active = true - err := arg.Parse(c.scan, c.getValue(arg)) - if err != nil { - return err - } - c.Path = append(c.Path, &Path{ - Parent: node, - Positional: arg, - remainder: c.scan.PeekAll(), - }) - positional++ - break - } - - // Assign token value to a branch name if tagged as an alias - // An alias will be ignored in the case of an existing command - cmds := make(map[string]bool) - for _, branch := range node.Children { - if branch.Type == CommandNode { - cmds[branch.Name] = true - } - } - for _, branch := range node.Children { - for _, a := range branch.Aliases { - _, ok := cmds[a] - if token.Value == a && !ok { - token.Value = branch.Name - break - } - } - } - - // After positional arguments have been consumed, check commands next... - for _, branch := range node.Children { - if branch.Type == CommandNode && !branch.Hidden { - candidates = append(candidates, branch.Name) - } - if branch.Type == CommandNode && branch.Name == token.Value { - c.scan.Pop() - c.Path = append(c.Path, &Path{ - Parent: node, - Command: branch, - Flags: branch.Flags, - remainder: c.scan.PeekAll(), - }) - return c.trace(branch) - } - } - - // Finally, check arguments. - for _, branch := range node.Children { - if branch.Type == ArgumentNode { - arg := branch.Argument - if err := arg.Parse(c.scan, c.getValue(arg)); err == nil { - c.Path = append(c.Path, &Path{ - Parent: node, - Argument: branch, - Flags: branch.Flags, - remainder: c.scan.PeekAll(), - }) - return c.trace(branch) - } - } - } - - // If there is a default command that allows args and nothing else - // matches, take the branch of the default command - if node.DefaultCmd != nil && node.DefaultCmd.Tag.Default == "withargs" { - c.Path = append(c.Path, &Path{ - Parent: node, - Command: node.DefaultCmd, - Flags: node.DefaultCmd.Flags, - remainder: c.scan.PeekAll(), - }) - return c.trace(node.DefaultCmd) - } - - return findPotentialCandidates(token.String(), candidates, "unexpected argument %s", token) - default: - return fmt.Errorf("unexpected token %s", token) - } - } - return c.maybeSelectDefault(flags, node) -} - -// IgnoreDefault can be implemented by flags that want to be applied before any default commands. -type IgnoreDefault interface { - IgnoreDefault() -} - -// End of the line, check for a default command, but only if we're not displaying help, -// otherwise we'd only ever display the help for the default command. -func (c *Context) maybeSelectDefault(flags []*Flag, node *Node) error { - for _, flag := range flags { - if _, ok := flag.Target.Interface().(IgnoreDefault); ok && flag.Set { - return nil - } - } - if node.DefaultCmd != nil { - c.Path = append(c.Path, &Path{ - Parent: node.DefaultCmd, - Command: node.DefaultCmd, - Flags: node.DefaultCmd.Flags, - remainder: c.scan.PeekAll(), - }) - } - return nil -} - -// Resolve walks through the traced path, applying resolvers to any unset flags. -func (c *Context) Resolve() error { - resolvers := c.combineResolvers() - if len(resolvers) == 0 { - return nil - } - - inserted := []*Path{} - for _, path := range c.Path { - for _, flag := range path.Flags { - // Flag has already been set on the command-line. - if _, ok := c.values[flag.Value]; ok { - continue - } - - // Pick the last resolved value. - var selected any - for _, resolver := range resolvers { - s, err := resolver.Resolve(c, path, flag) - if err != nil { - return fmt.Errorf("%s: %w", flag.ShortSummary(), err) - } - if s == nil { - continue - } - selected = s - } - - if selected == nil { - continue - } - - scan := Scan().PushTyped(selected, FlagValueToken) - delete(c.values, flag.Value) - err := flag.Parse(scan, c.getValue(flag.Value)) - if err != nil { - return err - } - inserted = append(inserted, &Path{ - Flag: flag, - Resolved: true, - remainder: c.scan.PeekAll(), - }) - } - } - c.Path = append(c.Path, inserted...) - return nil -} - -// Combine application-level resolvers and context resolvers. -func (c *Context) combineResolvers() []Resolver { - resolvers := []Resolver{} - resolvers = append(resolvers, c.Kong.resolvers...) - resolvers = append(resolvers, c.resolvers...) - return resolvers -} - -func (c *Context) getValue(value *Value) reflect.Value { - v, ok := c.values[value] - if !ok { - v = reflect.New(value.Target.Type()).Elem() - switch v.Kind() { - case reflect.Ptr: - v.Set(reflect.New(v.Type().Elem())) - case reflect.Slice: - v.Set(reflect.MakeSlice(v.Type(), 0, 0)) - case reflect.Map: - v.Set(reflect.MakeMap(v.Type())) - default: - } - c.values[value] = v - } - return v -} - -// ApplyDefaults if they are not already set. -func (c *Context) ApplyDefaults() error { - return Visit(c.Model.Node, func(node Visitable, next Next) error { - var value *Value - switch node := node.(type) { - case *Flag: - value = node.Value - case *Node: - value = node.Argument - case *Value: - value = node - default: - } - if value != nil { - if err := value.ApplyDefault(); err != nil { - return err - } - } - return next(nil) - }) -} - -// Apply traced context to the target grammar. -func (c *Context) Apply() (string, error) { - path := []string{} - - for _, trace := range c.Path { - var value *Value - switch { - case trace.App != nil: - case trace.Argument != nil: - path = append(path, "<"+trace.Argument.Name+">") - value = trace.Argument.Argument - case trace.Command != nil: - path = append(path, trace.Command.Name) - case trace.Flag != nil: - value = trace.Flag.Value - case trace.Positional != nil: - path = append(path, "<"+trace.Positional.Name+">") - value = trace.Positional - default: - panic("unsupported path ?!") - } - if value != nil { - value.Apply(c.getValue(value)) - } - } - - return strings.Join(path, " "), nil -} - -func flipBoolValue(value reflect.Value) error { - if value.Kind() == reflect.Bool { - value.SetBool(!value.Bool()) - return nil - } - - if value.Kind() == reflect.Ptr { - if !value.IsNil() { - return flipBoolValue(value.Elem()) - } - return nil - } - - return fmt.Errorf("cannot negate a value of %s", value.Type().String()) -} - -func (c *Context) parseFlag(flags []*Flag, match string) (err error) { - candidates := []string{} - - for _, flag := range flags { - long := "--" + flag.Name - matched := long == match - candidates = append(candidates, long) - if flag.Short != 0 { - short := "-" + string(flag.Short) - matched = matched || (short == match) - candidates = append(candidates, short) - } - for _, alias := range flag.Aliases { - alias = "--" + alias - matched = matched || (alias == match) - candidates = append(candidates, alias) - } - - neg := negatableFlagName(flag.Name, flag.Tag.Negatable) - if !matched && match != neg { - continue - } - // Found a matching flag. - c.scan.Pop() - if match == neg && flag.Tag.Negatable != "" { - flag.Negated = true - } - err := flag.Parse(c.scan, c.getValue(flag.Value)) - if err != nil { - var expected *expectedError - if errors.As(err, &expected) && expected.token.InferredType().IsAny(FlagToken, ShortFlagToken) { - return fmt.Errorf("%s; perhaps try %s=%q?", err.Error(), flag.ShortSummary(), expected.token) - } - return err - } - if flag.Negated { - value := c.getValue(flag.Value) - err := flipBoolValue(value) - if err != nil { - return err - } - flag.Value.Apply(value) - } - c.Path = append(c.Path, &Path{ - Flag: flag, - remainder: c.scan.PeekAll(), - }) - return nil - } - return &unknownFlagError{Cause: findPotentialCandidates(match, candidates, "unknown flag %s", match)} -} - -func isUnknownFlagError(err error) bool { - var unknown *unknownFlagError - return errors.As(err, &unknown) -} - -type unknownFlagError struct{ Cause error } - -func (e *unknownFlagError) Unwrap() error { return e.Cause } -func (e *unknownFlagError) Error() string { return e.Cause.Error() } - -// Call an arbitrary function filling arguments with bound values. -func (c *Context) Call(fn any, binds ...any) (out []any, err error) { - fv := reflect.ValueOf(fn) - bindings := c.Kong.bindings.clone().add(binds...).add(c).merge(c.bindings) - return callAnyFunction(fv, bindings) -} - -// RunNode calls the Run() method on an arbitrary node. -// -// This is useful in conjunction with Visit(), for dynamically running commands. -// -// Any passed values will be bindable to arguments of the target Run() method. Additionally, -// all parent nodes in the command structure will be bound. -func (c *Context) RunNode(node *Node, binds ...any) (err error) { - type targetMethod struct { - node *Node - method reflect.Value - binds bindings - } - methodBinds := c.Kong.bindings.clone().add(binds...).add(c).merge(c.bindings) - methods := []targetMethod{} - for i := 0; node != nil; i, node = i+1, node.Parent { - method := getMethod(node.Target, "Run") - methodBinds = methodBinds.clone() - for p := node; p != nil; p = p.Parent { - methodBinds = methodBinds.add(p.Target.Addr().Interface()) - // Try value and pointer to value. - for _, p := range []reflect.Value{p.Target, p.Target.Addr()} { - t := p.Type() - for i := 0; i < p.NumMethod(); i++ { - methodt := t.Method(i) - if strings.HasPrefix(methodt.Name, "Provide") { - method := p.Method(i) - if err := methodBinds.addProvider(method.Interface(), false /* singleton */); err != nil { - return fmt.Errorf("%s.%s: %w", t.Name(), methodt.Name, err) - } - } - } - } - } - if method.IsValid() { - methods = append(methods, targetMethod{node, method, methodBinds}) - } - } - if len(methods) == 0 { - return fmt.Errorf("no Run() method found in hierarchy of %s", c.Selected().Summary()) - } - for _, method := range methods { - if err = callFunction(method.method, method.binds); err != nil { - return err - } - } - return nil -} - -// Run executes the Run() method on the selected command, which must exist. -// -// Any passed values will be bindable to arguments of the target Run() method. Additionally, -// all parent nodes in the command structure will be bound. -func (c *Context) Run(binds ...any) (err error) { - node := c.Selected() - if node == nil { - if len(c.Path) == 0 { - return fmt.Errorf("no command selected") - } - selected := c.Path[0].Node() - if selected.Type == ApplicationNode { - method := getMethod(selected.Target, "Run") - if method.IsValid() { - node = selected - } - } - - if node == nil { - return fmt.Errorf("no command selected") - } - } - runErr := c.RunNode(node, binds...) - err = c.Kong.applyHook(c, "AfterRun") - return errors.Join(runErr, err) -} - -// PrintUsage to Kong's stdout. -// -// If summary is true, a summarised version of the help will be output. -func (c *Context) PrintUsage(summary bool) error { - options := c.helpOptions - options.Summary = summary - return c.help(options, c) -} - -func checkMissingFlags(flags []*Flag) error { - xorGroupSet := map[string]bool{} - xorGroup := map[string][]string{} - andGroupSet := map[string]bool{} - andGroup := map[string][]string{} - missing := []string{} - andGroupRequired := getRequiredAndGroupMap(flags) - for _, flag := range flags { - for _, and := range flag.And { - flag.Required = andGroupRequired[and] - } - if flag.Set { - for _, xor := range flag.Xor { - xorGroupSet[xor] = true - } - for _, and := range flag.And { - andGroupSet[and] = true - } - } - if !flag.Required || flag.Set { - continue - } - if len(flag.Xor) > 0 || len(flag.And) > 0 { - for _, xor := range flag.Xor { - if xorGroupSet[xor] { - continue - } - xorGroup[xor] = append(xorGroup[xor], flag.Summary()) - } - for _, and := range flag.And { - andGroup[and] = append(andGroup[and], flag.Summary()) - } - } else { - missing = append(missing, flag.Summary()) - } - } - for xor, flags := range xorGroup { - if !xorGroupSet[xor] && len(flags) > 1 { - missing = append(missing, strings.Join(flags, " or ")) - } - } - for _, flags := range andGroup { - if len(flags) > 1 { - missing = append(missing, strings.Join(flags, " and ")) - } - } - - if len(missing) == 0 { - return nil - } - - sort.Strings(missing) - - return fmt.Errorf("missing flags: %s", strings.Join(missing, ", ")) -} - -func getRequiredAndGroupMap(flags []*Flag) map[string]bool { - andGroupRequired := map[string]bool{} - for _, flag := range flags { - for _, and := range flag.And { - if flag.Required { - andGroupRequired[and] = true - } - } - } - return andGroupRequired -} - -func checkMissingChildren(node *Node) error { - missing := []string{} - - missingArgs := []string{} - for _, arg := range node.Positional { - if arg.Required && !arg.Set { - missingArgs = append(missingArgs, arg.Summary()) - } - } - if len(missingArgs) > 0 { - missing = append(missing, strconv.Quote(strings.Join(missingArgs, " "))) - } - - for _, child := range node.Children { - if child.Hidden { - continue - } - if child.Argument != nil { - if !child.Argument.Required { - continue - } - missing = append(missing, strconv.Quote(child.Summary())) - } else { - missing = append(missing, strconv.Quote(child.Name)) - } - } - if len(missing) == 0 { - return nil - } - - if len(missing) > 5 { - missing = append(missing[:5], "...") - } - if len(missing) == 1 { - return fmt.Errorf("expected %s", missing[0]) - } - return fmt.Errorf("expected one of %s", strings.Join(missing, ", ")) -} - -// If we're missing any positionals and they're required, return an error. -func checkMissingPositionals(positional int, values []*Value) error { - // All the positionals are in. - if positional >= len(values) { - return nil - } - - // We're low on supplied positionals, but the missing one is optional. - if !values[positional].Required { - return nil - } - - missing := []string{} - for ; positional < len(values); positional++ { - arg := values[positional] - // TODO(aat): Fix hardcoding of these env checks all over the place :\ - if len(arg.Tag.Envs) != 0 { - if atLeastOneEnvSet(arg.Tag.Envs) { - continue - } - } - missing = append(missing, "<"+arg.Name+">") - } - if len(missing) == 0 { - return nil - } - return fmt.Errorf("missing positional arguments %s", strings.Join(missing, " ")) -} - -func checkEnum(value *Value, target reflect.Value) error { - switch target.Kind() { - case reflect.Slice, reflect.Array: - for i := 0; i < target.Len(); i++ { - if err := checkEnum(value, target.Index(i)); err != nil { - return err - } - } - return nil - - case reflect.Map, reflect.Struct: - return errors.New("enum can only be applied to a slice or value") - - case reflect.Ptr: - if target.IsNil() { - return nil - } - return checkEnum(value, target.Elem()) - default: - enumSlice := value.EnumSlice() - v := fmt.Sprintf("%v", target) - enums := []string{} - for _, enum := range enumSlice { - if enum == v { - return nil - } - enums = append(enums, fmt.Sprintf("%q", enum)) - } - return fmt.Errorf("%s must be one of %s but got %q", value.ShortSummary(), strings.Join(enums, ","), fmt.Sprintf("%v", target.Interface())) - } -} - -func checkPassthroughArg(target reflect.Value) bool { - typ := target.Type() - switch typ.Kind() { - case reflect.Slice: - return typ.Elem().Kind() == reflect.String - default: - return false - } -} - -func checkXorDuplicatedAndAndMissing(paths []*Path) error { - errs := []string{} - if err := checkXorDuplicates(paths); err != nil { - errs = append(errs, err.Error()) - } - if err := checkAndMissing(paths); err != nil { - errs = append(errs, err.Error()) - } - if len(errs) > 0 { - return errors.New(strings.Join(errs, ", ")) - } - return nil -} - -func checkXorDuplicates(paths []*Path) error { - for _, path := range paths { - seen := map[string]*Flag{} - for _, flag := range path.Flags { - if !flag.Set { - continue - } - for _, xor := range flag.Xor { - if seen[xor] != nil { - return fmt.Errorf("--%s and --%s can't be used together", seen[xor].Name, flag.Name) - } - seen[xor] = flag - } - } - } - return nil -} - -func checkAndMissing(paths []*Path) error { - for _, path := range paths { - missingMsgs := []string{} - andGroups := map[string][]*Flag{} - for _, flag := range path.Flags { - for _, and := range flag.And { - andGroups[and] = append(andGroups[and], flag) - } - } - for _, flags := range andGroups { - oneSet := false - notSet := []*Flag{} - flagNames := []string{} - for _, flag := range flags { - flagNames = append(flagNames, flag.Name) - if flag.Set { - oneSet = true - } else { - notSet = append(notSet, flag) - } - } - if len(notSet) > 0 && oneSet { - missingMsgs = append(missingMsgs, fmt.Sprintf("--%s must be used together", strings.Join(flagNames, " and --"))) - } - } - if len(missingMsgs) > 0 { - return fmt.Errorf("%s", strings.Join(missingMsgs, ", ")) - } - } - return nil -} - -func findPotentialCandidates(needle string, haystack []string, format string, args ...any) error { - if len(haystack) == 0 { - return fmt.Errorf(format, args...) - } - closestCandidates := []string{} - for _, candidate := range haystack { - if strings.HasPrefix(candidate, needle) || levenshtein(candidate, needle) <= 2 { - closestCandidates = append(closestCandidates, fmt.Sprintf("%q", candidate)) - } - } - prefix := fmt.Sprintf(format, args...) - if len(closestCandidates) == 1 { - return fmt.Errorf("%s, did you mean %s?", prefix, closestCandidates[0]) - } else if len(closestCandidates) > 1 { - return fmt.Errorf("%s, did you mean one of %s?", prefix, strings.Join(closestCandidates, ", ")) - } - return fmt.Errorf("%s", prefix) -} - -type validatable interface{ Validate() error } -type extendedValidatable interface { - Validate(kctx *Context) error -} - -// Proxy a validatable function to the extendedValidatable interface -type validatableFunc func() error - -func (f validatableFunc) Validate(kctx *Context) error { return f() } - -func isValidatable(v reflect.Value) extendedValidatable { - if !v.IsValid() || (v.Kind() == reflect.Ptr || v.Kind() == reflect.Slice || v.Kind() == reflect.Map) && v.IsNil() { - return nil - } - if validate, ok := v.Interface().(validatable); ok { - return validatableFunc(validate.Validate) - } - if validate, ok := v.Interface().(extendedValidatable); ok { - return validate - } - if v.CanAddr() { - return isValidatable(v.Addr()) - } - return nil -} - -func atLeastOneEnvSet(envs []string) bool { - for _, env := range envs { - if _, ok := os.LookupEnv(env); ok { - return true - } - } - return false -} diff --git a/vendor/github.com/alecthomas/kong/defaults.go b/vendor/github.com/alecthomas/kong/defaults.go deleted file mode 100644 index 9489fb3..0000000 --- a/vendor/github.com/alecthomas/kong/defaults.go +++ /dev/null @@ -1,21 +0,0 @@ -package kong - -// ApplyDefaults if they are not already set. -func ApplyDefaults(target any, options ...Option) error { - app, err := New(target, options...) - if err != nil { - return err - } - ctx, err := Trace(app, nil) - if err != nil { - return err - } - err = ctx.Resolve() - if err != nil { - return err - } - if err = ctx.ApplyDefaults(); err != nil { - return err - } - return ctx.Validate() -} diff --git a/vendor/github.com/alecthomas/kong/doc.go b/vendor/github.com/alecthomas/kong/doc.go deleted file mode 100644 index 7e53da7..0000000 --- a/vendor/github.com/alecthomas/kong/doc.go +++ /dev/null @@ -1,32 +0,0 @@ -// Package kong aims to support arbitrarily complex command-line structures with as little developer effort as possible. -// -// Here's an example: -// -// shell rm [-f] [-r] ... -// shell ls [ ...] -// -// This can be represented by the following command-line structure: -// -// package main -// -// import "github.com/alecthomas/kong" -// -// var CLI struct { -// Rm struct { -// Force bool `short:"f" help:"Force removal."` -// Recursive bool `short:"r" help:"Recursively remove files."` -// -// Paths []string `arg help:"Paths to remove." type:"path"` -// } `cmd help:"Remove files."` -// -// Ls struct { -// Paths []string `arg optional help:"Paths to list." type:"path"` -// } `cmd help:"List paths."` -// } -// -// func main() { -// kong.Parse(&CLI) -// } -// -// See https://github.com/alecthomas/kong for details. -package kong diff --git a/vendor/github.com/alecthomas/kong/error.go b/vendor/github.com/alecthomas/kong/error.go deleted file mode 100644 index e79a15d..0000000 --- a/vendor/github.com/alecthomas/kong/error.go +++ /dev/null @@ -1,21 +0,0 @@ -package kong - -// ParseError is the error type returned by Kong.Parse(). -// -// It contains the parse Context that triggered the error. -type ParseError struct { - error - Context *Context - exitCode int -} - -// Unwrap returns the original cause of the error. -func (p *ParseError) Unwrap() error { return p.error } - -// ExitCode returns the status that Kong should exit with if it fails with a ParseError. -func (p *ParseError) ExitCode() int { - if p.exitCode == 0 { - return exitNotOk - } - return p.exitCode -} diff --git a/vendor/github.com/alecthomas/kong/exit.go b/vendor/github.com/alecthomas/kong/exit.go deleted file mode 100644 index 4925f48..0000000 --- a/vendor/github.com/alecthomas/kong/exit.go +++ /dev/null @@ -1,32 +0,0 @@ -package kong - -import "errors" - -const ( - exitOk = 0 - exitNotOk = 1 - - // Semantic exit codes from https://github.com/square/exit?tab=readme-ov-file#about - exitUsageError = 80 -) - -// ExitCoder is an interface that may be implemented by an error value to -// provide an integer exit code. The method ExitCode should return an integer -// that is intended to be used as the exit code for the application. -type ExitCoder interface { - ExitCode() int -} - -// exitCodeFromError returns the exit code for the given error. -// If err implements the exitCoder interface, the ExitCode method is called. -// Otherwise, exitCodeFromError returns 0 if err is nil, and 1 if it is not. -func exitCodeFromError(err error) int { - var e ExitCoder - if errors.As(err, &e) { - return e.ExitCode() - } else if err == nil { - return exitOk - } - - return exitNotOk -} diff --git a/vendor/github.com/alecthomas/kong/global.go b/vendor/github.com/alecthomas/kong/global.go deleted file mode 100644 index babe1e1..0000000 --- a/vendor/github.com/alecthomas/kong/global.go +++ /dev/null @@ -1,16 +0,0 @@ -package kong - -import ( - "os" -) - -// Parse constructs a new parser and parses the default command-line. -func Parse(cli any, options ...Option) *Context { - parser, err := New(cli, options...) - if err != nil { - panic(err) - } - ctx, err := parser.Parse(os.Args[1:]) - parser.FatalIfErrorf(err) - return ctx -} diff --git a/vendor/github.com/alecthomas/kong/guesswidth.go b/vendor/github.com/alecthomas/kong/guesswidth.go deleted file mode 100644 index dfdc3f5..0000000 --- a/vendor/github.com/alecthomas/kong/guesswidth.go +++ /dev/null @@ -1,10 +0,0 @@ -//go:build appengine || (!linux && !freebsd && !darwin && !dragonfly && !netbsd && !openbsd) -// +build appengine !linux,!freebsd,!darwin,!dragonfly,!netbsd,!openbsd - -package kong - -import "io" - -func guessWidth(w io.Writer) int { - return 80 -} diff --git a/vendor/github.com/alecthomas/kong/guesswidth_unix.go b/vendor/github.com/alecthomas/kong/guesswidth_unix.go deleted file mode 100644 index 0170055..0000000 --- a/vendor/github.com/alecthomas/kong/guesswidth_unix.go +++ /dev/null @@ -1,42 +0,0 @@ -//go:build (!appengine && linux) || freebsd || darwin || dragonfly || netbsd || openbsd -// +build !appengine,linux freebsd darwin dragonfly netbsd openbsd - -package kong - -import ( - "io" - "os" - "strconv" - "syscall" - "unsafe" -) - -func guessWidth(w io.Writer) int { - // check if COLUMNS env is set to comply with - // http://pubs.opengroup.org/onlinepubs/009604499/basedefs/xbd_chap08.html - colsStr := os.Getenv("COLUMNS") - if colsStr != "" { - if cols, err := strconv.Atoi(colsStr); err == nil { - return cols - } - } - - if t, ok := w.(*os.File); ok { - fd := t.Fd() - var dimensions [4]uint16 - - if _, _, err := syscall.Syscall6( - syscall.SYS_IOCTL, - uintptr(fd), //nolint: unconvert - uintptr(syscall.TIOCGWINSZ), - uintptr(unsafe.Pointer(&dimensions)), //nolint: gas - 0, 0, 0, - ); err == 0 { - if dimensions[1] == 0 { - return 80 - } - return int(dimensions[1]) - } - } - return 80 -} diff --git a/vendor/github.com/alecthomas/kong/help.go b/vendor/github.com/alecthomas/kong/help.go deleted file mode 100644 index 8da1555..0000000 --- a/vendor/github.com/alecthomas/kong/help.go +++ /dev/null @@ -1,576 +0,0 @@ -package kong - -import ( - "bytes" - "fmt" - "go/doc" - "io" - "strings" -) - -const ( - defaultIndent = 2 - defaultColumnPadding = 4 -) - -// Help flag. -type helpFlag bool - -func (h helpFlag) IgnoreDefault() {} - -func (h helpFlag) BeforeReset(ctx *Context) error { - options := ctx.Kong.helpOptions - options.Summary = false - err := ctx.Kong.help(options, ctx) - if err != nil { - return err - } - ctx.Kong.Exit(0) - return nil -} - -// HelpOptions for HelpPrinters. -type HelpOptions struct { - // Don't print top-level usage summary. - NoAppSummary bool - - // Write a one-line summary of the context. - Summary bool - - // Write help in a more compact, but still fully-specified, form. - Compact bool - - // Tree writes command chains in a tree structure instead of listing them separately. - Tree bool - - // Place the flags after the commands listing. - FlagsLast bool - - // Indenter modulates the given prefix for the next layer in the tree view. - // The following exported templates can be used: kong.SpaceIndenter, kong.LineIndenter, kong.TreeIndenter - // The kong.SpaceIndenter will be used by default. - Indenter HelpIndenter - - // Don't show the help associated with subcommands - NoExpandSubcommands bool - - // Clamp the help wrap width to a value smaller than the terminal width. - // If this is set to a non-positive number, the terminal width is used; otherwise, - // the min of this value or the terminal width is used. - WrapUpperBound int -} - -// Apply options to Kong as a configuration option. -func (h HelpOptions) Apply(k *Kong) error { - k.helpOptions = h - return nil -} - -// HelpProvider can be implemented by commands/args to provide detailed help. -type HelpProvider interface { - // This string is formatted by go/doc and thus has the same formatting rules. - Help() string -} - -// PlaceHolderProvider can be implemented by mappers to provide custom placeholder text. -type PlaceHolderProvider interface { - PlaceHolder(flag *Flag) string -} - -// HelpIndenter is used to indent new layers in the help tree. -type HelpIndenter func(prefix string) string - -// HelpPrinter is used to print context-sensitive help. -type HelpPrinter func(options HelpOptions, ctx *Context) error - -// HelpValueFormatter is used to format the help text of flags and positional arguments. -type HelpValueFormatter func(value *Value) string - -// DefaultHelpValueFormatter is the default HelpValueFormatter. -func DefaultHelpValueFormatter(value *Value) string { - if len(value.Tag.Envs) == 0 || HasInterpolatedVar(value.OrigHelp, "env") { - return value.Help - } - suffix := "(" + formatEnvs(value.Tag.Envs) + ")" - switch { - case strings.HasSuffix(value.Help, "."): - return value.Help[:len(value.Help)-1] + " " + suffix + "." - case value.Help == "": - return suffix - default: - return value.Help + " " + suffix - } -} - -// DefaultShortHelpPrinter is the default HelpPrinter for short help on error. -func DefaultShortHelpPrinter(options HelpOptions, ctx *Context) error { - w := newHelpWriter(ctx, options) - cmd := ctx.Selected() - app := ctx.Model - if cmd == nil { - w.Printf("Usage: %s%s", app.Name, app.Summary()) - w.Printf(`Run "%s --help" for more information.`, app.Name) - } else { - w.Printf("Usage: %s %s", app.Name, cmd.Summary()) - w.Printf(`Run "%s --help" for more information.`, cmd.FullPath()) - } - return w.Write(ctx.Stdout) -} - -// DefaultHelpPrinter is the default HelpPrinter. -func DefaultHelpPrinter(options HelpOptions, ctx *Context) error { - if ctx.Empty() { - options.Summary = false - } - w := newHelpWriter(ctx, options) - selected := ctx.Selected() - if selected == nil { - printApp(w, ctx.Model) - } else { - printCommand(w, ctx.Model, selected) - } - return w.Write(ctx.Stdout) -} - -func printApp(w *helpWriter, app *Application) { - if !w.NoAppSummary { - w.Printf("Usage: %s%s", app.Name, app.Summary()) - } - printNodeDetail(w, app.Node, true) - cmds := app.Leaves(true) - if len(cmds) > 0 && app.HelpFlag != nil { - w.Print("") - if w.Summary { - w.Printf(`Run "%s --help" for more information.`, app.Name) - } else { - w.Printf(`Run "%s --help" for more information on a command.`, app.Name) - } - } -} - -func printCommand(w *helpWriter, app *Application, cmd *Command) { - if !w.NoAppSummary { - w.Printf("Usage: %s %s", app.Name, cmd.Summary()) - } - printNodeDetail(w, cmd, true) - if w.Summary && app.HelpFlag != nil { - w.Print("") - w.Printf(`Run "%s --help" for more information.`, cmd.FullPath()) - } -} - -func printNodeDetail(w *helpWriter, node *Node, hide bool) { - if node.Help != "" { - w.Print("") - w.Wrap(node.Help) - } - if w.Summary { - return - } - if node.Detail != "" { - w.Print("") - w.Wrap(node.Detail) - } - if len(node.Positional) > 0 { - w.Print("") - w.Print("Arguments:") - writePositionals(w.Indent(), node.Positional) - } - printFlags := func() { - if flags := node.AllFlags(true); len(flags) > 0 { - groupedFlags := collectFlagGroups(flags) - for _, group := range groupedFlags { - w.Print("") - if group.Metadata.Title != "" { - w.Wrap(group.Metadata.Title) - } - if group.Metadata.Description != "" { - w.Indent().Wrap(group.Metadata.Description) - w.Print("") - } - writeFlags(w.Indent(), group.Flags) - } - } - } - if !w.FlagsLast { - printFlags() - } - var cmds []*Node - if w.NoExpandSubcommands { - cmds = node.Children - } else { - cmds = node.Leaves(hide) - } - if len(cmds) > 0 { - iw := w.Indent() - if w.Tree { - w.Print("") - w.Print("Commands:") - writeCommandTree(iw, node) - } else { - groupedCmds := collectCommandGroups(cmds) - for _, group := range groupedCmds { - w.Print("") - if group.Metadata.Title != "" { - w.Wrap(group.Metadata.Title) - } - if group.Metadata.Description != "" { - w.Indent().Wrap(group.Metadata.Description) - w.Print("") - } - - if w.Compact { - writeCompactCommandList(group.Commands, iw) - } else { - writeCommandList(group.Commands, iw) - } - } - } - } - if w.FlagsLast { - printFlags() - } -} - -func writeCommandList(cmds []*Node, iw *helpWriter) { - for i, cmd := range cmds { - if cmd.Hidden { - continue - } - printCommandSummary(iw, cmd) - if i != len(cmds)-1 { - iw.Print("") - } - } -} - -func writeCompactCommandList(cmds []*Node, iw *helpWriter) { - rows := [][2]string{} - for _, cmd := range cmds { - if cmd.Hidden { - continue - } - rows = append(rows, [2]string{cmd.Path(), cmd.Help}) - } - writeTwoColumns(iw, rows) -} - -func writeCommandTree(w *helpWriter, node *Node) { - rows := make([][2]string, 0, len(node.Children)*2) - for i, cmd := range node.Children { - if cmd.Hidden { - continue - } - rows = append(rows, w.CommandTree(cmd, "")...) - if i != len(node.Children)-1 { - rows = append(rows, [2]string{"", ""}) - } - } - writeTwoColumns(w, rows) -} - -type helpFlagGroup struct { - Metadata *Group - Flags [][]*Flag -} - -func collectFlagGroups(flags [][]*Flag) []helpFlagGroup { - // Group keys in order of appearance. - groups := []*Group{} - // Flags grouped by their group key. - flagsByGroup := map[string][][]*Flag{} - - for _, levelFlags := range flags { - levelFlagsByGroup := map[string][]*Flag{} - - for _, flag := range levelFlags { - key := "" - if flag.Group != nil { - key = flag.Group.Key - groupAlreadySeen := false - for _, group := range groups { - if key == group.Key { - groupAlreadySeen = true - break - } - } - if !groupAlreadySeen { - groups = append(groups, flag.Group) - } - } - - levelFlagsByGroup[key] = append(levelFlagsByGroup[key], flag) - } - - for key, flags := range levelFlagsByGroup { - flagsByGroup[key] = append(flagsByGroup[key], flags) - } - } - - out := []helpFlagGroup{} - // Ungrouped flags are always displayed first. - if ungroupedFlags, ok := flagsByGroup[""]; ok { - out = append(out, helpFlagGroup{ - Metadata: &Group{Title: "Flags:"}, - Flags: ungroupedFlags, - }) - } - for _, group := range groups { - out = append(out, helpFlagGroup{Metadata: group, Flags: flagsByGroup[group.Key]}) - } - return out -} - -type helpCommandGroup struct { - Metadata *Group - Commands []*Node -} - -func collectCommandGroups(nodes []*Node) []helpCommandGroup { - // Groups in order of appearance. - groups := []*Group{} - // Nodes grouped by their group key. - nodesByGroup := map[string][]*Node{} - - for _, node := range nodes { - key := "" - if group := node.ClosestGroup(); group != nil { - key = group.Key - if _, ok := nodesByGroup[key]; !ok { - groups = append(groups, group) - } - } - nodesByGroup[key] = append(nodesByGroup[key], node) - } - - out := []helpCommandGroup{} - // Ungrouped nodes are always displayed first. - if ungroupedNodes, ok := nodesByGroup[""]; ok { - out = append(out, helpCommandGroup{ - Metadata: &Group{Title: "Commands:"}, - Commands: ungroupedNodes, - }) - } - for _, group := range groups { - out = append(out, helpCommandGroup{Metadata: group, Commands: nodesByGroup[group.Key]}) - } - return out -} - -func printCommandSummary(w *helpWriter, cmd *Command) { - w.Print(cmd.Summary()) - if cmd.Help != "" { - w.Indent().Wrap(cmd.Help) - } -} - -type helpWriter struct { - indent string - width int - lines *[]string - helpFormatter HelpValueFormatter - HelpOptions -} - -func newHelpWriter(ctx *Context, options HelpOptions) *helpWriter { - lines := []string{} - wrapWidth := guessWidth(ctx.Stdout) - if options.WrapUpperBound > 0 && wrapWidth > options.WrapUpperBound { - wrapWidth = options.WrapUpperBound - } - w := &helpWriter{ - indent: "", - width: wrapWidth, - lines: &lines, - helpFormatter: ctx.Kong.helpFormatter, - HelpOptions: options, - } - return w -} - -func (h *helpWriter) Printf(format string, args ...any) { - h.Print(fmt.Sprintf(format, args...)) -} - -func (h *helpWriter) Print(text string) { - *h.lines = append(*h.lines, strings.TrimRight(h.indent+text, " ")) -} - -// Indent returns a new helpWriter indented by two characters. -func (h *helpWriter) Indent() *helpWriter { - return &helpWriter{indent: h.indent + " ", lines: h.lines, width: h.width - 2, HelpOptions: h.HelpOptions, helpFormatter: h.helpFormatter} -} - -func (h *helpWriter) String() string { - return strings.Join(*h.lines, "\n") -} - -func (h *helpWriter) Write(w io.Writer) error { - for _, line := range *h.lines { - _, err := io.WriteString(w, line+"\n") - if err != nil { - return err - } - } - return nil -} - -func (h *helpWriter) Wrap(text string) { - w := bytes.NewBuffer(nil) - doc.ToText(w, strings.TrimSpace(text), "", " ", h.width) //nolint:staticcheck // cross-package links not possible - for _, line := range strings.Split(strings.TrimSpace(w.String()), "\n") { - h.Print(line) - } -} - -func writePositionals(w *helpWriter, args []*Positional) { - rows := [][2]string{} - for _, arg := range args { - rows = append(rows, [2]string{arg.Summary(), w.helpFormatter(arg)}) - } - writeTwoColumns(w, rows) -} - -func writeFlags(w *helpWriter, groups [][]*Flag) { - rows := [][2]string{} - haveShort := false - for _, group := range groups { - for _, flag := range group { - if flag.Short != 0 { - haveShort = true - break - } - } - } - for i, group := range groups { - if i > 0 { - rows = append(rows, [2]string{"", ""}) - } - for _, flag := range group { - if !flag.Hidden { - rows = append(rows, [2]string{formatFlag(haveShort, flag), w.helpFormatter(flag.Value)}) - } - } - } - writeTwoColumns(w, rows) -} - -func writeTwoColumns(w *helpWriter, rows [][2]string) { - maxLeft := 375 * w.width / 1000 - if maxLeft < 30 { - maxLeft = 30 - } - // Find size of first column. - leftSize := 0 - for _, row := range rows { - if c := len(row[0]); c > leftSize && c < maxLeft { - leftSize = c - } - } - - offsetStr := strings.Repeat(" ", leftSize+defaultColumnPadding) - - for _, row := range rows { - buf := bytes.NewBuffer(nil) - doc.ToText(buf, row[1], "", strings.Repeat(" ", defaultIndent), w.width-leftSize-defaultColumnPadding) //nolint:staticcheck // cross-package links not possible - lines := strings.Split(strings.TrimRight(buf.String(), "\n"), "\n") - - line := fmt.Sprintf("%-*s", leftSize, row[0]) - if len(row[0]) < maxLeft { - line += fmt.Sprintf("%*s%s", defaultColumnPadding, "", lines[0]) - lines = lines[1:] - } - w.Print(line) - for _, line := range lines { - w.Printf("%s%s", offsetStr, line) - } - } -} - -// haveShort will be true if there are short flags present at all in the help. Useful for column alignment. -func formatFlag(haveShort bool, flag *Flag) string { - flagString := "" - name := flag.Name - isBool := flag.IsBool() - isCounter := flag.IsCounter() - - short := "" - if flag.Short != 0 { - short = "-" + string(flag.Short) + ", " - } else if haveShort { - short = " " - } - - if isBool && flag.Tag.Negatable == negatableDefault { - name = "[no-]" + name - } else if isBool && flag.Tag.Negatable != "" { - name += "/" + flag.Tag.Negatable - } - - flagString += fmt.Sprintf("%s--%s", short, name) - - if !isBool && !isCounter { - flagString += fmt.Sprintf("=%s", flag.FormatPlaceHolder()) - } - return flagString -} - -// CommandTree creates a tree with the given node name as root and its children's arguments and sub commands as leaves. -func (h *HelpOptions) CommandTree(node *Node, prefix string) (rows [][2]string) { - var nodeName string - switch node.Type { - default: - nodeName += prefix + node.Name - if len(node.Aliases) != 0 { - nodeName += fmt.Sprintf(" (%s)", strings.Join(node.Aliases, ",")) - } - case ArgumentNode: - nodeName += prefix + "<" + node.Name + ">" - } - rows = append(rows, [2]string{nodeName, node.Help}) - if h.Indenter == nil { - prefix = SpaceIndenter(prefix) - } else { - prefix = h.Indenter(prefix) - } - for _, arg := range node.Positional { - rows = append(rows, [2]string{prefix + arg.Summary(), arg.Help}) - } - for _, subCmd := range node.Children { - if subCmd.Hidden { - continue - } - rows = append(rows, h.CommandTree(subCmd, prefix)...) - } - return -} - -// SpaceIndenter adds a space indent to the given prefix. -func SpaceIndenter(prefix string) string { - return prefix + strings.Repeat(" ", defaultIndent) -} - -// LineIndenter adds line points to every new indent. -func LineIndenter(prefix string) string { - if prefix == "" { - return "- " - } - return strings.Repeat(" ", defaultIndent) + prefix -} - -// TreeIndenter adds line points to every new indent and vertical lines to every layer. -func TreeIndenter(prefix string) string { - if prefix == "" { - return "|- " - } - return "|" + strings.Repeat(" ", defaultIndent) + prefix -} - -func formatEnvs(envs []string) string { - formatted := make([]string, len(envs)) - for i := range envs { - formatted[i] = "$" + envs[i] - } - - return strings.Join(formatted, ", ") -} diff --git a/vendor/github.com/alecthomas/kong/hooks.go b/vendor/github.com/alecthomas/kong/hooks.go deleted file mode 100644 index e95d21b..0000000 --- a/vendor/github.com/alecthomas/kong/hooks.go +++ /dev/null @@ -1,32 +0,0 @@ -package kong - -// BeforeReset is a documentation-only interface describing hooks that run before defaults values are applied. -type BeforeReset interface { - // This is not the correct signature - see README for details. - BeforeReset(args ...any) error -} - -// BeforeResolve is a documentation-only interface describing hooks that run before resolvers are applied. -type BeforeResolve interface { - // This is not the correct signature - see README for details. - BeforeResolve(args ...any) error -} - -// BeforeApply is a documentation-only interface describing hooks that run before values are set. -type BeforeApply interface { - // This is not the correct signature - see README for details. - BeforeApply(args ...any) error -} - -// AfterApply is a documentation-only interface describing hooks that run after values are set. -type AfterApply interface { - // This is not the correct signature - see README for details. - AfterApply(args ...any) error -} - -// AfterRun is a documentation-only interface describing hooks that run after Run() returns. -type AfterRun interface { - // This is not the correct signature - see README for details. - // AfterRun is called after Run() returns. - AfterRun(args ...any) error -} diff --git a/vendor/github.com/alecthomas/kong/interpolate.go b/vendor/github.com/alecthomas/kong/interpolate.go deleted file mode 100644 index e811632..0000000 --- a/vendor/github.com/alecthomas/kong/interpolate.go +++ /dev/null @@ -1,52 +0,0 @@ -package kong - -import ( - "fmt" - "regexp" -) - -var interpolationRegex = regexp.MustCompile(`(\$\$)|((?:\${([[:alpha:]_][[:word:]]*))(?:=([^}]+))?})|(\$)|([^$]+)`) - -// HasInterpolatedVar returns true if the variable "v" is interpolated in "s". -func HasInterpolatedVar(s string, v string) bool { - matches := interpolationRegex.FindAllStringSubmatch(s, -1) - for _, match := range matches { - if name := match[3]; name == v { - return true - } - } - return false -} - -// Interpolate variables from vars into s for substrings in the form ${var} or ${var=default}. -func interpolate(s string, vars Vars, updatedVars map[string]string) (string, error) { - out := "" - matches := interpolationRegex.FindAllStringSubmatch(s, -1) - if len(matches) == 0 { - return s, nil - } - for key, val := range updatedVars { - if vars[key] != val { - vars = vars.CloneWith(updatedVars) - break - } - } - for _, match := range matches { - if dollar := match[1]; dollar != "" { - out += "$" - } else if name := match[3]; name != "" { - value, ok := vars[name] - if !ok { - // No default value. - if match[4] == "" { - return "", fmt.Errorf("undefined variable ${%s}", name) - } - value = match[4] - } - out += value - } else { - out += match[0] - } - } - return out, nil -} diff --git a/vendor/github.com/alecthomas/kong/kong.go b/vendor/github.com/alecthomas/kong/kong.go deleted file mode 100644 index 2334a8a..0000000 --- a/vendor/github.com/alecthomas/kong/kong.go +++ /dev/null @@ -1,500 +0,0 @@ -package kong - -import ( - "errors" - "fmt" - "io" - "os" - "path/filepath" - "reflect" - "regexp" - "strings" -) - -var ( - callbackReturnSignature = reflect.TypeOf((*error)(nil)).Elem() -) - -func failField(parent reflect.Value, field reflect.StructField, format string, args ...any) error { - name := parent.Type().Name() - if name == "" { - name = "" - } - return fmt.Errorf("%s.%s: %s", name, field.Name, fmt.Sprintf(format, args...)) -} - -// Must creates a new Parser or panics if there is an error. -func Must(ast any, options ...Option) *Kong { - k, err := New(ast, options...) - if err != nil { - panic(err) - } - return k -} - -type usageOnError int - -const ( - shortUsage usageOnError = iota + 1 - fullUsage -) - -// Kong is the main parser type. -type Kong struct { - // Grammar model. - Model *Application - - // Termination function (defaults to os.Exit) - Exit func(int) - - Stdout io.Writer - Stderr io.Writer - - bindings bindings - loader ConfigurationLoader - resolvers []Resolver - registry *Registry - ignoreFields []*regexp.Regexp - - noDefaultHelp bool - allowHyphenated bool - usageOnError usageOnError - help HelpPrinter - shortHelp HelpPrinter - helpFormatter HelpValueFormatter - helpOptions HelpOptions - helpFlag *Flag - groups []Group - vars Vars - flagNamer func(string) string - - // Set temporarily by Options. These are applied after build(). - postBuildOptions []Option - embedded []embedded - dynamicCommands []*dynamicCommand - - hooks map[string][]reflect.Value -} - -// New creates a new Kong parser on grammar. -// -// See the README (https://github.com/alecthomas/kong) for usage instructions. -func New(grammar any, options ...Option) (*Kong, error) { - k := &Kong{ - Exit: os.Exit, - Stdout: os.Stdout, - Stderr: os.Stderr, - registry: NewRegistry().RegisterDefaults(), - vars: Vars{}, - bindings: bindings{}, - hooks: make(map[string][]reflect.Value), - helpFormatter: DefaultHelpValueFormatter, - ignoreFields: make([]*regexp.Regexp, 0), - flagNamer: func(s string) string { - return strings.ToLower(dashedString(s)) - }, - } - - options = append(options, Bind(k)) - - for _, option := range options { - if err := option.Apply(k); err != nil { - return nil, err - } - } - - if k.help == nil { - k.help = DefaultHelpPrinter - } - - if k.shortHelp == nil { - k.shortHelp = DefaultShortHelpPrinter - } - - model, err := build(k, grammar) - if err != nil { - return k, err - } - model.Name = filepath.Base(os.Args[0]) - k.Model = model - k.Model.HelpFlag = k.helpFlag - - // Embed any embedded structs. - for _, embed := range k.embedded { - tag, err := parseTagString(strings.Join(embed.tags, " ")) - if err != nil { - return nil, err - } - tag.Embed = true - v := reflect.Indirect(reflect.ValueOf(embed.strct)) - node, err := buildNode(k, v, CommandNode, tag, map[string]bool{}) - if err != nil { - return nil, err - } - for _, child := range node.Children { - child.Parent = k.Model.Node - k.Model.Children = append(k.Model.Children, child) - } - k.Model.Flags = append(k.Model.Flags, node.Flags...) - } - - // Synthesise command nodes. - for _, dcmd := range k.dynamicCommands { - tag, terr := parseTagString(strings.Join(dcmd.tags, " ")) - if terr != nil { - return nil, terr - } - tag.Name = dcmd.name - tag.Help = dcmd.help - tag.Group = dcmd.group - tag.Cmd = true - v := reflect.Indirect(reflect.ValueOf(dcmd.cmd)) - err = buildChild(k, k.Model.Node, CommandNode, reflect.Value{}, reflect.StructField{ - Name: dcmd.name, - Type: v.Type(), - }, v, tag, dcmd.name, map[string]bool{}) - if err != nil { - return nil, err - } - } - - for _, option := range k.postBuildOptions { - if err = option.Apply(k); err != nil { - return nil, err - } - } - k.postBuildOptions = nil - - if err = k.interpolate(k.Model.Node); err != nil { - return nil, err - } - - k.bindings.add(k.vars) - - if err = checkOverlappingXorAnd(k); err != nil { - return nil, err - } - - return k, nil -} - -func checkOverlappingXorAnd(k *Kong) error { - xorGroups := map[string][]string{} - andGroups := map[string][]string{} - for _, flag := range k.Model.Node.Flags { - for _, xor := range flag.Xor { - xorGroups[xor] = append(xorGroups[xor], flag.Name) - } - for _, and := range flag.And { - andGroups[and] = append(andGroups[and], flag.Name) - } - } - for xor, xorSet := range xorGroups { - for and, andSet := range andGroups { - overlappingEntries := []string{} - for _, xorTag := range xorSet { - for _, andTag := range andSet { - if xorTag == andTag { - overlappingEntries = append(overlappingEntries, xorTag) - } - } - } - if len(overlappingEntries) > 1 { - return fmt.Errorf("invalid xor and combination, %s and %s overlap with more than one: %s", xor, and, overlappingEntries) - } - } - } - return nil -} - -type varStack []Vars - -func (v *varStack) head() Vars { return (*v)[len(*v)-1] } -func (v *varStack) pop() { *v = (*v)[:len(*v)-1] } -func (v *varStack) push(vars Vars) Vars { - if len(*v) != 0 { - vars = (*v)[len(*v)-1].CloneWith(vars) - } - *v = append(*v, vars) - return vars -} - -// Interpolate variables into model. -func (k *Kong) interpolate(node *Node) (err error) { - stack := varStack{} - return Visit(node, func(node Visitable, next Next) error { - switch node := node.(type) { - case *Node: - vars := stack.push(node.Vars()) - node.Help, err = interpolate(node.Help, vars, nil) - if err != nil { - return fmt.Errorf("help for %s: %s", node.Path(), err) - } - err = next(nil) - stack.pop() - return err - - case *Value: - return next(k.interpolateValue(node, stack.head())) - } - return next(nil) - }) -} - -func (k *Kong) interpolateValue(value *Value, vars Vars) (err error) { - if len(value.Tag.Vars) > 0 { - vars = vars.CloneWith(value.Tag.Vars) - } - if varsContributor, ok := value.Mapper.(VarsContributor); ok { - vars = vars.CloneWith(varsContributor.Vars(value)) - } - - if value.Enum, err = interpolate(value.Enum, vars, nil); err != nil { - return fmt.Errorf("enum for %s: %s", value.Summary(), err) - } - - if value.Default, err = interpolate(value.Default, vars, nil); err != nil { - return fmt.Errorf("default value for %s: %s", value.Summary(), err) - } - if value.Enum, err = interpolate(value.Enum, vars, nil); err != nil { - return fmt.Errorf("enum value for %s: %s", value.Summary(), err) - } - updatedVars := map[string]string{ - "default": value.Default, - "enum": value.Enum, - } - if value.Flag != nil { - for i, env := range value.Flag.Envs { - if value.Flag.Envs[i], err = interpolate(env, vars, updatedVars); err != nil { - return fmt.Errorf("env value for %s: %s", value.Summary(), err) - } - } - value.Tag.Envs = value.Flag.Envs - updatedVars["env"] = "" - if len(value.Flag.Envs) != 0 { - updatedVars["env"] = value.Flag.Envs[0] - } - - value.Flag.PlaceHolder, err = interpolate(value.Flag.PlaceHolder, vars, updatedVars) - if err != nil { - return fmt.Errorf("placeholder value for %s: %s", value.Summary(), err) - } - } - value.Help, err = interpolate(value.Help, vars, updatedVars) - if err != nil { - return fmt.Errorf("help for %s: %s", value.Summary(), err) - } - return nil -} - -// Provide additional builtin flags, if any. -func (k *Kong) extraFlags() []*Flag { - if k.noDefaultHelp { - return nil - } - var helpTarget helpFlag - value := reflect.ValueOf(&helpTarget).Elem() - helpFlag := &Flag{ - Short: 'h', - Value: &Value{ - Name: "help", - Help: "Show context-sensitive help.", - OrigHelp: "Show context-sensitive help.", - Target: value, - Tag: &Tag{}, - Mapper: k.registry.ForValue(value), - DefaultValue: reflect.ValueOf(false), - }, - } - helpFlag.Flag = helpFlag - k.helpFlag = helpFlag - return []*Flag{helpFlag} -} - -// Parse arguments into target. -// -// The return Context can be used to further inspect the parsed command-line, to format help, to find the -// selected command, to run command Run() methods, and so on. See Context and README for more information. -// -// Will return a ParseError if a *semantically* invalid command-line is encountered (as opposed to a syntactically -// invalid one, which will report a normal error). -func (k *Kong) Parse(args []string) (ctx *Context, err error) { - ctx, err = Trace(k, args) - if err != nil { // Trace is not expected to return an err - return nil, &ParseError{error: err, Context: ctx, exitCode: exitUsageError} - } - if ctx.Error != nil { - return nil, &ParseError{error: ctx.Error, Context: ctx, exitCode: exitUsageError} - } - if err = k.applyHook(ctx, "BeforeReset"); err != nil { - return nil, &ParseError{error: err, Context: ctx} - } - if err = ctx.Reset(); err != nil { - return nil, &ParseError{error: err, Context: ctx} - } - if err = k.applyHook(ctx, "BeforeResolve"); err != nil { - return nil, &ParseError{error: err, Context: ctx} - } - if err = ctx.Resolve(); err != nil { - return nil, &ParseError{error: err, Context: ctx} - } - if err = k.applyHook(ctx, "BeforeApply"); err != nil { - return nil, &ParseError{error: err, Context: ctx} - } - if _, err = ctx.Apply(); err != nil { // Apply is not expected to return an err - return nil, &ParseError{error: err, Context: ctx} - } - if err = ctx.Validate(); err != nil { - return nil, &ParseError{error: err, Context: ctx, exitCode: exitUsageError} - } - if err = k.applyHook(ctx, "AfterApply"); err != nil { - return nil, &ParseError{error: err, Context: ctx} - } - return ctx, nil -} - -func (k *Kong) applyHook(ctx *Context, name string) error { - for _, trace := range ctx.Path { - var value reflect.Value - switch { - case trace.App != nil: - value = trace.App.Target - case trace.Argument != nil: - value = trace.Argument.Target - case trace.Command != nil: - value = trace.Command.Target - case trace.Positional != nil: - value = trace.Positional.Target - case trace.Flag != nil: - value = trace.Flag.Value.Target - default: - panic("unsupported Path") - } - for _, method := range k.getMethods(value, name) { - binds := k.bindings.clone() - binds.add(ctx, trace) - binds.add(trace.Node().Vars().CloneWith(k.vars)) - binds.merge(ctx.bindings) - if err := callFunction(method, binds); err != nil { - return err - } - } - } - // Path[0] will always be the app root. - return k.applyHookToDefaultFlags(ctx, ctx.Path[0].Node(), name) -} - -func (k *Kong) getMethods(value reflect.Value, name string) []reflect.Value { - return append( - // Identify callbacks by reflecting on value - getMethods(value, name), - - // Identify callbacks that were registered with a kong.Option - k.hooks[name]..., - ) -} - -// Call hook on any unset flags with default values. -func (k *Kong) applyHookToDefaultFlags(ctx *Context, node *Node, name string) error { - if node == nil { - return nil - } - return Visit(node, func(n Visitable, next Next) error { - node, ok := n.(*Node) - if !ok { - return next(nil) - } - binds := k.bindings.clone().add(ctx).add(node.Vars().CloneWith(k.vars)) - for _, flag := range node.Flags { - if !flag.HasDefault || ctx.values[flag.Value].IsValid() || !flag.Target.IsValid() { - continue - } - for _, method := range getMethods(flag.Target, name) { - path := &Path{Flag: flag} - if err := callFunction(method, binds.clone().add(path)); err != nil { - return next(err) - } - } - } - return next(nil) - }) -} - -func formatMultilineMessage(w io.Writer, leaders []string, format string, args ...any) { - lines := strings.Split(strings.TrimRight(fmt.Sprintf(format, args...), "\n"), "\n") - leader := "" - for _, l := range leaders { - if l == "" { - continue - } - leader += l + ": " - } - fmt.Fprintf(w, "%s%s\n", leader, lines[0]) - for _, line := range lines[1:] { - fmt.Fprintf(w, "%*s%s\n", len(leader), " ", line) - } -} - -// Printf writes a message to Kong.Stdout with the application name prefixed. -func (k *Kong) Printf(format string, args ...any) *Kong { - formatMultilineMessage(k.Stdout, []string{k.Model.Name}, format, args...) - return k -} - -// Errorf writes a message to Kong.Stderr with the application name prefixed. -func (k *Kong) Errorf(format string, args ...any) *Kong { - formatMultilineMessage(k.Stderr, []string{k.Model.Name, "error"}, format, args...) - return k -} - -// Fatalf writes a message to Kong.Stderr with the application name prefixed then exits with status 1. -func (k *Kong) Fatalf(format string, args ...any) { - k.Errorf(format, args...) - k.Exit(1) -} - -// FatalIfErrorf terminates with an error message if err != nil. -// If the error implements the ExitCoder interface, the ExitCode() method is called and -// the application exits with that status. Otherwise, the application exits with status 1. -func (k *Kong) FatalIfErrorf(err error, args ...any) { - if err == nil { - return - } - msg := err.Error() - if len(args) > 0 { - msg = fmt.Sprintf(args[0].(string), args[1:]...) + ": " + err.Error() //nolint - } - // Maybe display usage information. - var parseErr *ParseError - if errors.As(err, &parseErr) { - switch k.usageOnError { - case fullUsage: - _ = k.help(k.helpOptions, parseErr.Context) - fmt.Fprintln(k.Stdout) - case shortUsage: - _ = k.shortHelp(k.helpOptions, parseErr.Context) - fmt.Fprintln(k.Stdout) - } - } - k.Errorf("%s", msg) - k.Exit(exitCodeFromError(err)) -} - -// LoadConfig from path using the loader configured via Configuration(loader). -// -// "path" will have ~ and any variables expanded. -func (k *Kong) LoadConfig(path string) (Resolver, error) { - var err error - path = ExpandPath(path) - path, err = interpolate(path, k.vars, nil) - if err != nil { - return nil, err - } - r, err := os.Open(path) //nolint: gas - if err != nil { - return nil, err - } - defer r.Close() - - return k.loader(r) -} diff --git a/vendor/github.com/alecthomas/kong/kong.png b/vendor/github.com/alecthomas/kong/kong.png deleted file mode 100644 index 151fb08dbd4858c4fbebb76f46bf6c6535f8f099..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 68610 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%0Au<7CSicYLeX!B6|rppsfl&f{V4* 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