Relates to 840220c (#90)
This change adds support for hooks to be called on fields
that are tagged with `embed:""`.
### Use case
If a command has several subcommands,
many (but not all) of which need the same external resource,
this allows defining the flag-level inputs for that resource centrally,
and then using `embed:""` in any command that needs that resource.
For example, imagine:
```go
type githubClientProvider struct {
Token string `name:"github-token" env:"GITHUB_TOKEN"`
URL string `name:"github-url" env:"GITHUB_URL"`
}
func (g *githubClientProvider) BeforeApply(kctx *kong.Context) error {
return kctx.BindToProvider(func() (*github.Client, error) {
return github.NewClient(...), nil
})
}
```
Then, any command that needs GitHub client will add this field,
any other resource providers it needs,
and add parameters to its `Run` method to accept those resources:
```go
type listUsersCmd struct {
GitHub githubClientProvider `embed:""`
S3 s3ClientProvider `embed:""`
}
func (l *listUsersCmd) Run(gh *github.Client, s3 *s3.Client) error {
...
}
```
### Alternatives
It is possible to do the same today if the `*Provider` struct above
is actually a Go embed instead of a Kong embed, *and* it is exported.
```
type GitHubClientProvider struct{ ... }
type listUsersCmd struct {
GithubClientProvider
S3ClientProvider
}
```
The difference is whether the struct defining the flags
is required to be exported or not.
`passthrough:""` or `passthrough:"all"` (the default) will pass through
all further arguments including unrecognised flags.
`passthrough:"partial"` will validate flags up until the `--` or the
first positional argument, then pass through all subsequent flags and
arguments.
This allows provider functions to accept parameters that are injected by other
bindings or binding providers, eg. call the provider function with the root CLI
struct (which is automatically bound by Kong):
kong.BindToProvider(func(cli *CLI) (*Injected, error) { ... })
Given a grammar like this:
```golang
var cli struct {
Args []string `arg:"" optional:"" passthrough:""`
}
```
If Kong parses `cli foo -- bar`, it will populate `Args` with `[]string{"foo", "--", "bar"}` (including "`--`").
However, if Kong parses `cli -- foo bar`, will populate `Args` with `[]string{"foo", "bar"}` (leaving off `"--"`).
This differs from the behavior of a passthrough Command, where `"--"` is included with the args in both cases.
There are 3 places where `c.endParsing()` is called
1. When `node.Passthrough` is true: https://github.com/alecthomas/kong/blob/5f9c5cc822bdb888a3671c44d4688a6f602ecb90/context.go#L366-L368
2. When `arg.Passthrough` is true: https://github.com/alecthomas/kong/blob/5f9c5cc822bdb888a3671c44d4688a6f602ecb90/context.go#L451-L453
3. When `"--"` is encountered: https://github.com/alecthomas/kong/blob/5f9c5cc822bdb888a3671c44d4688a6f602ecb90/context.go#L384-L387
The first two do not also pop any tokens. The third one does.
This commit makes `c.scan.Pop()` conditional, skipping it when the next positional argument is passthrough.
I believe this will cause Kong to behave a little more consistently — and from my perspective, `--` is relevant for args intended to be passed through! — but it will change the behavior of existing projects that use `arg:"" passthrough:""`.
* fix: Check if negatable duplicates another flag
Add a check for flags with the `negatable` option if the negative flag
conflicts with another tag, such as:
Flag bool `negatable:""`
NoFlag bool
The flag `--no-flag` is ambiguous in this scenario.
* feat: Make negatable flag name customisable
Allow a value on the `negatable` tag to specify a flag name to use for
negation instead of using `--no-<flag-name>` as the flag.
e.g.
Approve bool `default:"true",negatable:"deny"`
This example will allow `--deny` to set the `Approve` field to false.
* Docs: Clean and group description
* Feat: Add check for overlapping xor and and groups
Co-authored-by: inful <jone.marius@vign.es>
* Chore: Rewrite overlap err to avoid duplicated words
---------
Co-authored-by: inful <jone.marius@vign.es>
* Feat: Add xand group and check for missing
* Fix: Split and combine err in TestMultiand for consistency
* Feat: Check missing required flags in xand groups
* Feat: Handle combined xor and xand
* Docs: Add info about combined xand and required use
* Docs: Fix language error in xand description
Co-authored-by: Stautis <thkrst@gmail.com>
* Feat: Rename xand to and
* Refactor: Switch from fmt.Sprintf to err.Error
* Refactor: Get requiredAndGroup map in separate function
---------
Co-authored-by: Stautis <thkrst@gmail.com>
* feat: allow non-structs to be used as commands
This small MR allows using the func-to-interface trick to implement a command (see commandFunc in kong_test.go).
This is useful e.g. for commands that have no flags or arguments of their own, but instead receive all required information via bound parameters.
* fix: check DynamicCommand is runnable when adding
* ci: Add a test for positional args that are passthrough on a command that isn't passthrough
* fix: When a Grammar combines flags with passthrough args, see if an unrecognized flag may be treated as a positional argument
Given a grammar like this:
```golang
var cli struct {
Args []string `arg:"" optional:"" passthrough:""`
}
```
The first positional argument implies that it was preceded by `--`, so subsequent flags are not parsed.
If Kong parses `cli 1 --unknown 3`, it will populate `Args` with `[]string{"1", "--unknown", "3"}`.
However, if Kong parses `cli --unknown 2 3`, it will fail saying that `--unknown` is an unrecognized flag.
This commit changes the parser so that if an unknown flag _could_ be treated as the first passthrough argument, it is.
After this change, if Kong parses `cli --unknown 2 3`, it will populate `Args` with `[]string{"--unknown", "2", "3"}`.
* ci: Skip the `maintidx` linter for `trace()`