⬆️ update dependencies, ✨ implement user store
This commit is contained in:
1
vendor/github.com/alecthomas/kong/.gitignore
generated
vendored
Normal file
1
vendor/github.com/alecthomas/kong/.gitignore
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
bin
|
||||
42
vendor/github.com/alecthomas/kong/.golangci.yml
generated
vendored
Normal file
42
vendor/github.com/alecthomas/kong/.golangci.yml
generated
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
run:
|
||||
tests: true
|
||||
|
||||
output:
|
||||
print-issued-lines: false
|
||||
|
||||
linters:
|
||||
enable-all: true
|
||||
disable:
|
||||
- maligned
|
||||
- lll
|
||||
- gochecknoglobals
|
||||
- wsl
|
||||
- funlen
|
||||
- gocognit
|
||||
- gomnd
|
||||
- goprintffuncname
|
||||
|
||||
linters-settings:
|
||||
govet:
|
||||
check-shadowing: true
|
||||
dupl:
|
||||
threshold: 100
|
||||
goconst:
|
||||
min-len: 5
|
||||
min-occurrences: 3
|
||||
gocyclo:
|
||||
min-complexity: 20
|
||||
|
||||
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'
|
||||
19
vendor/github.com/alecthomas/kong/COPYING
generated
vendored
Normal file
19
vendor/github.com/alecthomas/kong/COPYING
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
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.
|
||||
536
vendor/github.com/alecthomas/kong/README.md
generated
vendored
Normal file
536
vendor/github.com/alecthomas/kong/README.md
generated
vendored
Normal file
@@ -0,0 +1,536 @@
|
||||
<!-- markdownlint-disable MD013 MD033 -->
|
||||
<p align="center"><img width="90%" src="kong.png" /></p>
|
||||
|
||||
# Kong is a command-line parser for Go
|
||||
[](http://godoc.org/github.com/alecthomas/kong) [](https://circleci.com/gh/alecthomas/kong) [](https://goreportcard.com/report/github.com/alecthomas/kong) [](https://gophers.slack.com/messages/CN9DS8YF3)
|
||||
|
||||
[TOC levels=2-3 numbered]: # "#### Table of Contents"
|
||||
|
||||
#### Table of Contents
|
||||
1. [Introduction](#introduction)
|
||||
1. [Help](#help)
|
||||
1. [Command handling](#command-handling)
|
||||
1. [Switch on the command string](#switch-on-the-command-string)
|
||||
1. [Attach a `Run(...) error` method to each command](#attach-a-run-error-method-to-each-command)
|
||||
1. [Hooks: BeforeResolve(), BeforeApply(), AfterApply() and the Bind() option](#hooks-beforeresolve-beforeapply-afterapply-and-the-bind-option)
|
||||
1. [Flags](#flags)
|
||||
1. [Commands and sub-commands](#commands-and-sub-commands)
|
||||
1. [Branching positional arguments](#branching-positional-arguments)
|
||||
1. [Terminating positional arguments](#terminating-positional-arguments)
|
||||
1. [Slices](#slices)
|
||||
1. [Maps](#maps)
|
||||
1. [Custom named decoders](#custom-named-decoders)
|
||||
1. [Custom decoders (mappers)](#custom-decoders-mappers)
|
||||
1. [Supported tags](#supported-tags)
|
||||
1. [Variable interpolation](#variable-interpolation)
|
||||
1. [Modifying Kong's behaviour](#modifying-kongs-behaviour)
|
||||
1. [`Name(help)` and `Description(help)` - set the application name description](#namehelp-and-descriptionhelp---set-the-application-name-description)
|
||||
1. [`Configuration(loader, paths...)` - load defaults from configuration files](#configurationloader-paths---load-defaults-from-configuration-files)
|
||||
1. [`Resolver(...)` - support for default values from external sources](#resolver---support-for-default-values-from-external-sources)
|
||||
1. [`*Mapper(...)` - customising how the command-line is mapped to Go values](#mapper---customising-how-the-command-line-is-mapped-to-go-values)
|
||||
1. [`ConfigureHelp(HelpOptions)` and `Help(HelpFunc)` - customising help](#configurehelphelpoptions-and-helphelpfunc---customising-help)
|
||||
1. [`Bind(...)` - bind values for callback hooks and Run() methods](#bind---bind-values-for-callback-hooks-and-run-methods)
|
||||
1. [Other options](#other-options)
|
||||
|
||||
|
||||
## 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] <paths> ...
|
||||
shell ls [<paths> ...]
|
||||
|
||||
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 <path>":
|
||||
case "ls":
|
||||
default:
|
||||
panic(ctx.Command())
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Help
|
||||
|
||||
Help is automatically generated. With no other arguments provided, help will display a full summary of all available commands.
|
||||
|
||||
eg.
|
||||
|
||||
$ shell --help
|
||||
usage: shell <command>
|
||||
|
||||
A shell-like example app.
|
||||
|
||||
Flags:
|
||||
--help Show context-sensitive help.
|
||||
--debug Debug mode.
|
||||
|
||||
Commands:
|
||||
rm <path> ...
|
||||
Remove files.
|
||||
|
||||
ls [<path> ...]
|
||||
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 <paths> ...
|
||||
|
||||
Remove files.
|
||||
|
||||
Arguments:
|
||||
<paths> ... Paths to remove.
|
||||
|
||||
Flags:
|
||||
--debug Debug mode.
|
||||
|
||||
-f, --force Force removal.
|
||||
-r, --recursive Recursively remove files.
|
||||
|
||||
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,
|
||||
|
||||
|
||||
## 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/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 <path>":
|
||||
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: BeforeResolve(), BeforeApply(), AfterApply() and the Bind() option
|
||||
|
||||
If a node in the grammar has a `BeforeResolve(...)`, `BeforeApply(...) error` and/or `AfterApply(...) error` method, those methods will be called before validation/assignment and after validation/assignment, respectively.
|
||||
|
||||
The `--help` flag is implemented with a `BeforeApply` hook.
|
||||
|
||||
Arguments to hooks are provided via the `Run(...)` method or `Bind(...)` option. `*Kong`, `*Context` and `*Path` are also bound and finally, hooks can also contribute bindings via `kong.Context.Bind()` and `kong.Context.BindTo()`.
|
||||
|
||||
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(ioutil.Discard, "", log.LstdFlags)
|
||||
|
||||
ctx := kong.Parse(&cli, kong.Bind(logger))
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
## 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.
|
||||
|
||||
## 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 <name> to <name>
|
||||
|
||||
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.
|
||||
|
||||
## Terminating positional arguments
|
||||
|
||||
If a [mapped type](#mapper---customising-how-the-command-line-is-mapped-to-go-values) is tagged with `arg` it will be treated as the final positional values to be parsed on the command line.
|
||||
|
||||
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:"<rune>"` 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 <file> <file> ...
|
||||
|
||||
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 <key>=<value> <key>=<value> ...
|
||||
|
||||
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"`.
|
||||
|
||||
## Custom named decoders
|
||||
|
||||
Kong includes a number of builtin custom type mappers. These can be used by
|
||||
specifying the tag `type:"<type>"`. They are registered with the option
|
||||
function `NamedMapper(name, mapper)`.
|
||||
|
||||
| Name | Description
|
||||
|-------------------|---------------------------------------------------
|
||||
| `path` | A path. ~ expansion is applied.
|
||||
| `existingfile` | An existing file. ~ expansion is applied. `-` is accepted for stdin.
|
||||
| `existingdir` | An existing directory. ~ expansion is applied.
|
||||
| `counter` | Increment a numeric field. Useful for `-vvv`. Can accept `-s`, `--long` or `--long=N`.
|
||||
|
||||
|
||||
Slices and maps treat type tags specially. For slices, the `type:""` tag
|
||||
specifies the element type. For maps, the tag has the format
|
||||
`tag:"[<key>]:[<value>]"` where either may be omitted.
|
||||
|
||||
|
||||
## Custom decoders (mappers)
|
||||
|
||||
Any field implementing `encoding.TextUnmarshaler` or `json.Unmarshaler` will use those interfaces
|
||||
for decoding values.
|
||||
|
||||
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.
|
||||
`env:"X"` | Specify envar to use for default value.
|
||||
`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 text.
|
||||
`default:"X"` | Default value.
|
||||
`default:"1"` | On a command, make it the default.
|
||||
`short:"X"` | Short name, if flag.
|
||||
`required` | If present, flag/arg is required.
|
||||
`optional` | If present, flag/arg is optional.
|
||||
`hidden` | If present, command or flag is hidden.
|
||||
`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.
|
||||
`group:"X"` | Logical group for a flag or command.
|
||||
`xor:"X"` | Exclusive OR group for flags. Only one flag in the group can be used which is restricted within the same command.
|
||||
`prefix:"X"` | Prefix for all sub-flags.
|
||||
`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.
|
||||
`-` | Ignore the field. Useful for adding non-CLI fields to a configuration struct.
|
||||
|
||||
## Variable interpolation
|
||||
|
||||
Kong supports limited variable interpolation into help strings, enum lists and
|
||||
default values.
|
||||
|
||||
Variables are in the form:
|
||||
|
||||
${<name>}
|
||||
${<name>=<default>}
|
||||
|
||||
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}
|
||||
|
||||
eg.
|
||||
|
||||
```go
|
||||
type cli struct {
|
||||
Config string `type:"path" default:"${config_file}"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
kong.Parse(&cli,
|
||||
kong.Vars{
|
||||
"config_file": "~/.app.conf",
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
## Modifying Kong's behaviour
|
||||
|
||||
Each Kong parser can be configured via functional options passed to `New(cli interface{}, 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#L103) for an example of how the JSON file is structured.
|
||||
|
||||
### `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 knows how to map command-line input to Go.
|
||||
type Mapper interface {
|
||||
// Decode scan into target.
|
||||
//
|
||||
// "ctx" contains context about the value being decoded that may be useful
|
||||
// to some mapperss.
|
||||
Decode(ctx *MapperContext, scan *Scanner, 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:"<name>"`.
|
||||
2. `KindMapper(reflect.Kind, Mapper)`.
|
||||
3. `TypeMapper(reflect.Type, Mapper)`.
|
||||
4. `ValueMapper(interface{}, 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 `PrintHelp` for an example.
|
||||
3. Use `HelpFormatter(HelpValueFormatter)` if you want to just customize the help text that is accompanied by flags and arguments.
|
||||
|
||||
### `Bind(...)` - bind values for callback hooks and Run() methods
|
||||
|
||||
See the [section on hooks](#hooks-beforeresolve-beforeapply-afterapply-and-the-bind-option) for details.
|
||||
|
||||
### Other options
|
||||
|
||||
The full set of options can be found [here](https://godoc.org/github.com/alecthomas/kong#Option).
|
||||
211
vendor/github.com/alecthomas/kong/build.go
generated
vendored
Normal file
211
vendor/github.com/alecthomas/kong/build.go
generated
vendored
Normal file
@@ -0,0 +1,211 @@
|
||||
package kong
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func build(k *Kong, ast interface{}) (app *Application, err error) {
|
||||
defer catch(&err)
|
||||
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 := buildNode(k, iv, ApplicationNode, seenFlags)
|
||||
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) (out []flattenedField) {
|
||||
v = reflect.Indirect(v)
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
ft := v.Type().Field(i)
|
||||
fv := v.Field(i)
|
||||
tag := parseTag(fv, ft)
|
||||
if tag.Ignored {
|
||||
continue
|
||||
}
|
||||
if ft.Anonymous || tag.Embed {
|
||||
if fv.Kind() == reflect.Interface {
|
||||
fv = fv.Elem()
|
||||
}
|
||||
sub := flattenedFields(fv)
|
||||
for _, subf := range sub {
|
||||
// Assign parent if it's not already set.
|
||||
if subf.tag.Group == "" {
|
||||
subf.tag.Group = tag.Group
|
||||
}
|
||||
// Accumulate prefixes.
|
||||
subf.tag.Prefix = tag.Prefix + subf.tag.Prefix
|
||||
// Combine parent vars.
|
||||
subf.tag.Vars = tag.Vars.CloneWith(subf.tag.Vars)
|
||||
}
|
||||
out = append(out, sub...)
|
||||
continue
|
||||
}
|
||||
if !fv.CanSet() {
|
||||
continue
|
||||
}
|
||||
out = append(out, flattenedField{field: ft, value: fv, tag: tag})
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func buildNode(k *Kong, v reflect.Value, typ NodeType, seenFlags map[string]bool) *Node {
|
||||
node := &Node{
|
||||
Type: typ,
|
||||
Target: v,
|
||||
Tag: newEmptyTag(),
|
||||
}
|
||||
for _, field := range flattenedFields(v) {
|
||||
ft := field.field
|
||||
fv := field.value
|
||||
|
||||
tag := field.tag
|
||||
name := tag.Name
|
||||
if name == "" {
|
||||
name = tag.Prefix + strings.ToLower(dashedString(ft.Name))
|
||||
} else {
|
||||
name = tag.Prefix + name
|
||||
}
|
||||
|
||||
// Nested structs are either commands or args, unless they implement the Mapper interface.
|
||||
if ft.Type.Kind() == reflect.Struct && (tag.Cmd || tag.Arg) && k.registry.ForValue(fv) == nil {
|
||||
typ := CommandNode
|
||||
if tag.Arg {
|
||||
typ = ArgumentNode
|
||||
}
|
||||
buildChild(k, node, typ, v, ft, fv, tag, name, seenFlags)
|
||||
} else {
|
||||
buildField(k, node, v, ft, fv, tag, name, seenFlags)
|
||||
}
|
||||
}
|
||||
|
||||
// "Unsee" flags.
|
||||
for _, flag := range node.Flags {
|
||||
delete(seenFlags, flag.Name)
|
||||
}
|
||||
|
||||
// Scan through argument positionals to ensure optional is never before a required.
|
||||
last := true
|
||||
for i, p := range node.Positional {
|
||||
if !last && p.Required {
|
||||
fail("argument %q can not be required after an optional", p.Name)
|
||||
}
|
||||
|
||||
last = p.Required
|
||||
p.Position = i
|
||||
}
|
||||
|
||||
return node
|
||||
}
|
||||
|
||||
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) {
|
||||
child := buildNode(k, fv, typ, seenFlags)
|
||||
child.Tag = tag
|
||||
child.Parent = node
|
||||
child.Help = tag.Help
|
||||
child.Hidden = tag.Hidden
|
||||
child.Group = tag.Group
|
||||
|
||||
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 {
|
||||
fail("positional branch %s.%s must have at least one child positional argument named %q",
|
||||
v.Type().Name(), ft.Name, name)
|
||||
}
|
||||
|
||||
value := child.Positional[0]
|
||||
child.Positional = child.Positional[1:]
|
||||
if child.Help == "" {
|
||||
child.Help = value.Help
|
||||
}
|
||||
|
||||
child.Name = value.Name
|
||||
if child.Name != name {
|
||||
fail("first field in positional branch %s.%s must have the same name as the parent field (%s).",
|
||||
v.Type().Name(), ft.Name, child.Name)
|
||||
}
|
||||
|
||||
child.Argument = value
|
||||
} else {
|
||||
child.Name = name
|
||||
}
|
||||
node.Children = append(node.Children, child)
|
||||
|
||||
if len(child.Positional) > 0 && len(child.Children) > 0 {
|
||||
fail("can't mix positional arguments and branching arguments on %s.%s", v.Type().Name(), ft.Name)
|
||||
}
|
||||
}
|
||||
|
||||
func buildField(k *Kong, node *Node, v reflect.Value, ft reflect.StructField, fv reflect.Value, tag *Tag, name string, seenFlags map[string]bool) {
|
||||
mapper := k.registry.ForNamedValue(tag.Type, fv)
|
||||
if mapper == nil {
|
||||
fail("unsupported field type %s.%s (of type %s)", v.Type(), ft.Name, ft.Type)
|
||||
}
|
||||
|
||||
value := &Value{
|
||||
Name: name,
|
||||
Help: tag.Help,
|
||||
Default: tag.Default,
|
||||
DefaultValue: reflect.New(fv.Type()).Elem(),
|
||||
Mapper: mapper,
|
||||
Tag: tag,
|
||||
Target: fv,
|
||||
Enum: tag.Enum,
|
||||
|
||||
// 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] {
|
||||
fail("duplicate flag --%s", value.Name)
|
||||
}
|
||||
seenFlags[value.Name] = true
|
||||
flag := &Flag{
|
||||
Value: value,
|
||||
Short: tag.Short,
|
||||
PlaceHolder: tag.PlaceHolder,
|
||||
Env: tag.Env,
|
||||
Group: tag.Group,
|
||||
Xor: tag.Xor,
|
||||
Hidden: tag.Hidden,
|
||||
}
|
||||
value.Flag = flag
|
||||
node.Flags = append(node.Flags, flag)
|
||||
}
|
||||
}
|
||||
76
vendor/github.com/alecthomas/kong/callbacks.go
generated
vendored
Normal file
76
vendor/github.com/alecthomas/kong/callbacks.go
generated
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
package kong
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type bindings map[reflect.Type]func() (reflect.Value, error)
|
||||
|
||||
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 ...interface{}) bindings {
|
||||
for _, v := range values {
|
||||
v := v
|
||||
b[reflect.TypeOf(v)] = func() (reflect.Value, error) { return reflect.ValueOf(v), nil }
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
func callMethod(name string, v, f reflect.Value, bindings bindings) error {
|
||||
in := []reflect.Value{}
|
||||
t := f.Type()
|
||||
if t.NumOut() != 1 || t.Out(0) != callbackReturnSignature {
|
||||
return fmt.Errorf("return value of %T.%s() must be exactly \"error\"", v.Type(), name)
|
||||
}
|
||||
for i := 0; i < t.NumIn(); i++ {
|
||||
pt := t.In(i)
|
||||
if argf, ok := bindings[pt]; ok {
|
||||
argv, err := argf()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
in = append(in, argv)
|
||||
} else {
|
||||
return fmt.Errorf("couldn't find binding of type %s for parameter %d of %s.%s(), use kong.Bind(%s)", pt, i, v.Type(), name, pt)
|
||||
}
|
||||
}
|
||||
out := f.Call(in)
|
||||
if out[0].IsNil() {
|
||||
return nil
|
||||
}
|
||||
return out[0].Interface().(error)
|
||||
}
|
||||
90
vendor/github.com/alecthomas/kong/camelcase.go
generated
vendored
Normal file
90
vendor/github.com/alecthomas/kong/camelcase.go
generated
vendored
Normal file
@@ -0,0 +1,90 @@
|
||||
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
|
||||
}
|
||||
787
vendor/github.com/alecthomas/kong/context.go
generated
vendored
Normal file
787
vendor/github.com/alecthomas/kong/context.go
generated
vendored
Normal file
@@ -0,0 +1,787 @@
|
||||
package kong
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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) {
|
||||
c := &Context{
|
||||
Kong: k,
|
||||
Args: args,
|
||||
Path: []*Path{
|
||||
{App: k.Model, Flags: k.Model.Flags},
|
||||
},
|
||||
values: map[*Value]reflect.Value{},
|
||||
scan: Scan(args...),
|
||||
bindings: bindings{},
|
||||
}
|
||||
c.Error = c.trace(c.Model.Node)
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// Bind adds bindings to the Context.
|
||||
func (c *Context) Bind(args ...interface{}) {
|
||||
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 interface{}) {
|
||||
valueOf := reflect.ValueOf(impl)
|
||||
c.bindings[reflect.TypeOf(iface).Elem()] = func() (reflect.Value, error) { return valueOf, nil }
|
||||
}
|
||||
|
||||
// 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 {
|
||||
if value, ok := node.(*Value); ok {
|
||||
if value.Enum != "" && (!value.Required || value.Default != "") {
|
||||
if err := checkEnum(value, value.Target); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return next(nil)
|
||||
})
|
||||
if err != nil {
|
||||
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 := checkXorDuplicates(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) interface{} {
|
||||
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) trace(node *Node) (err error) { // nolint: gocyclo
|
||||
positional := 0
|
||||
|
||||
flags := []*Flag{}
|
||||
for _, group := range node.AllFlags(false) {
|
||||
flags = append(flags, group...)
|
||||
}
|
||||
|
||||
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.scan.Pop()
|
||||
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)
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return err
|
||||
}
|
||||
|
||||
case ShortFlagToken:
|
||||
if err := c.parseFlag(flags, token.String()); err != nil {
|
||||
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]
|
||||
err := arg.Parse(c.scan, c.getValue(arg))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.Path = append(c.Path, &Path{
|
||||
Parent: node,
|
||||
Positional: arg,
|
||||
})
|
||||
positional++
|
||||
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,
|
||||
})
|
||||
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,
|
||||
})
|
||||
return c.trace(branch)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return findPotentialCandidates(token.String(), candidates, "unexpected argument %s", token)
|
||||
default:
|
||||
return fmt.Errorf("unexpected token %s", token)
|
||||
}
|
||||
}
|
||||
return c.maybeSelectDefault(flags, node)
|
||||
}
|
||||
|
||||
// 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 flag.Name == "help" && flag.Set {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
var defaultNode *Path
|
||||
for _, child := range node.Children {
|
||||
if child.Type == CommandNode && child.Tag.Default != "" {
|
||||
if defaultNode != nil {
|
||||
return fmt.Errorf("can't have more than one default command under %s", node.Summary())
|
||||
}
|
||||
defaultNode = &Path{
|
||||
Parent: child,
|
||||
Command: child,
|
||||
Flags: child.Flags,
|
||||
}
|
||||
}
|
||||
}
|
||||
if defaultNode != nil {
|
||||
c.Path = append(c.Path, defaultNode)
|
||||
}
|
||||
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 interface{}
|
||||
for _, resolver := range resolvers {
|
||||
s, err := resolver.Resolve(c, path, flag)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, flag.ShortSummary())
|
||||
}
|
||||
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,
|
||||
})
|
||||
}
|
||||
}
|
||||
c.Path = append(inserted, c.Path...)
|
||||
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()
|
||||
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 (c *Context) parseFlag(flags []*Flag, match string) (err error) {
|
||||
defer catch(&err)
|
||||
candidates := []string{}
|
||||
for _, flag := range flags {
|
||||
long := "--" + flag.Name
|
||||
short := "-" + string(flag.Short)
|
||||
candidates = append(candidates, long)
|
||||
if flag.Short != 0 {
|
||||
candidates = append(candidates, short)
|
||||
}
|
||||
if short != match && long != match {
|
||||
continue
|
||||
}
|
||||
// Found a matching flag.
|
||||
c.scan.Pop()
|
||||
err := flag.Parse(c.scan, c.getValue(flag.Value))
|
||||
if err != nil {
|
||||
if e, ok := errors.Cause(err).(*expectedError); ok && e.token.InferredType().IsAny(FlagToken, ShortFlagToken) {
|
||||
return errors.Errorf("%s; perhaps try %s=%q?", err, flag.ShortSummary(), e.token)
|
||||
}
|
||||
return err
|
||||
}
|
||||
c.Path = append(c.Path, &Path{Flag: flag})
|
||||
return nil
|
||||
}
|
||||
return findPotentialCandidates(match, candidates, "unknown flag %s", match)
|
||||
}
|
||||
|
||||
// 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 ...interface{}) (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())
|
||||
}
|
||||
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())
|
||||
}
|
||||
_, err = c.Apply()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, method := range methods {
|
||||
if err = callMethod("Run", method.node.Target, 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 ...interface{}) (err error) {
|
||||
defer catch(&err)
|
||||
node := c.Selected()
|
||||
if node == nil {
|
||||
return fmt.Errorf("no command selected")
|
||||
}
|
||||
return c.RunNode(node, binds...)
|
||||
}
|
||||
|
||||
// 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
|
||||
_ = c.help(options, c)
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkMissingFlags(flags []*Flag) error {
|
||||
missing := []string{}
|
||||
for _, flag := range flags {
|
||||
if !flag.Required || flag.Set {
|
||||
continue
|
||||
}
|
||||
missing = append(missing, flag.Summary())
|
||||
}
|
||||
if len(missing) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("missing flags: %s", strings.Join(missing, ", "))
|
||||
}
|
||||
|
||||
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, " ")))
|
||||
}
|
||||
|
||||
haveDefault := 0
|
||||
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 {
|
||||
if child.Tag.Default != "" {
|
||||
if len(child.Children) > 0 {
|
||||
return fmt.Errorf("default command %s must not have subcommands or arguments", child.Summary())
|
||||
}
|
||||
haveDefault++
|
||||
}
|
||||
missing = append(missing, strconv.Quote(child.Name))
|
||||
}
|
||||
}
|
||||
if haveDefault > 1 {
|
||||
return fmt.Errorf("more than one default command found under %s", node.Summary())
|
||||
}
|
||||
if len(missing) == 0 || haveDefault > 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++ {
|
||||
missing = append(missing, "<"+values[positional].Name+">")
|
||||
}
|
||||
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.Errorf("enum can only be applied to a slice or value")
|
||||
|
||||
default:
|
||||
enumMap := value.EnumMap()
|
||||
v := fmt.Sprintf("%v", target)
|
||||
if enumMap[v] {
|
||||
return nil
|
||||
}
|
||||
enums := []string{}
|
||||
for enum := range enumMap {
|
||||
enums = append(enums, fmt.Sprintf("%q", enum))
|
||||
}
|
||||
sort.Strings(enums)
|
||||
return fmt.Errorf("%s must be one of %s but got %q", value.ShortSummary(), strings.Join(enums, ","), target.Interface())
|
||||
}
|
||||
}
|
||||
|
||||
func checkXorDuplicates(paths []*Path) error {
|
||||
for _, path := range paths {
|
||||
seen := map[string]*Flag{}
|
||||
for _, flag := range path.Flags {
|
||||
if !flag.Set {
|
||||
continue
|
||||
}
|
||||
if flag.Xor == "" {
|
||||
continue
|
||||
}
|
||||
if seen[flag.Xor] != nil {
|
||||
return fmt.Errorf("--%s and --%s can't be used together", seen[flag.Xor].Name, flag.Name)
|
||||
}
|
||||
seen[flag.Xor] = flag
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func findPotentialCandidates(needle string, haystack []string, format string, args ...interface{}) 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)
|
||||
}
|
||||
21
vendor/github.com/alecthomas/kong/defaults.go
generated
vendored
Normal file
21
vendor/github.com/alecthomas/kong/defaults.go
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
package kong
|
||||
|
||||
// ApplyDefaults if they are not already set.
|
||||
func ApplyDefaults(target interface{}, 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()
|
||||
}
|
||||
32
vendor/github.com/alecthomas/kong/doc.go
generated
vendored
Normal file
32
vendor/github.com/alecthomas/kong/doc.go
generated
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
// 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] <paths> ...
|
||||
// shell ls [<paths> ...]
|
||||
//
|
||||
// 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
|
||||
12
vendor/github.com/alecthomas/kong/error.go
generated
vendored
Normal file
12
vendor/github.com/alecthomas/kong/error.go
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
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
|
||||
}
|
||||
|
||||
// Cause returns the original cause of the error.
|
||||
func (p *ParseError) Cause() error { return p.error }
|
||||
16
vendor/github.com/alecthomas/kong/global.go
generated
vendored
Normal file
16
vendor/github.com/alecthomas/kong/global.go
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
package kong
|
||||
|
||||
import (
|
||||
"os"
|
||||
)
|
||||
|
||||
// Parse constructs a new parser and parses the default command-line.
|
||||
func Parse(cli interface{}, 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
|
||||
}
|
||||
10
vendor/github.com/alecthomas/kong/go.mod
generated
vendored
Normal file
10
vendor/github.com/alecthomas/kong/go.mod
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
module github.com/alecthomas/kong
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/pkg/errors v0.8.1
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/stretchr/testify v1.2.2
|
||||
)
|
||||
|
||||
go 1.13
|
||||
8
vendor/github.com/alecthomas/kong/go.sum
generated
vendored
Normal file
8
vendor/github.com/alecthomas/kong/go.sum
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
9
vendor/github.com/alecthomas/kong/guesswidth.go
generated
vendored
Normal file
9
vendor/github.com/alecthomas/kong/guesswidth.go
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
// +build appengine !linux,!freebsd,!darwin,!dragonfly,!netbsd,!openbsd
|
||||
|
||||
package kong
|
||||
|
||||
import "io"
|
||||
|
||||
func guessWidth(w io.Writer) int {
|
||||
return 80
|
||||
}
|
||||
41
vendor/github.com/alecthomas/kong/guesswidth_unix.go
generated
vendored
Normal file
41
vendor/github.com/alecthomas/kong/guesswidth_unix.go
generated
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
// +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
|
||||
}
|
||||
416
vendor/github.com/alecthomas/kong/help.go
generated
vendored
Normal file
416
vendor/github.com/alecthomas/kong/help.go
generated
vendored
Normal file
@@ -0,0 +1,416 @@
|
||||
package kong
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/doc"
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultIndent = 2
|
||||
defaultColumnPadding = 4
|
||||
)
|
||||
|
||||
// Help flag.
|
||||
type helpValue bool
|
||||
|
||||
func (h helpValue) BeforeApply(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
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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 value.Tag.Env == "" {
|
||||
return value.Help
|
||||
}
|
||||
suffix := "($" + value.Tag.Env + ")"
|
||||
switch {
|
||||
case strings.HasSuffix(value.Help, "."):
|
||||
return value.Help[:len(value.Help)-1] + " " + suffix + "."
|
||||
case value.Help == "":
|
||||
return suffix
|
||||
default:
|
||||
return value.Help + " " + suffix
|
||||
}
|
||||
}
|
||||
|
||||
// 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 <command> --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)
|
||||
}
|
||||
if flags := node.AllFlags(true); len(flags) > 0 {
|
||||
w.Print("")
|
||||
w.Print("Flags:")
|
||||
writeFlags(w.Indent(), flags)
|
||||
}
|
||||
cmds := node.Leaves(hide)
|
||||
if len(cmds) > 0 {
|
||||
w.Print("")
|
||||
w.Print("Commands:")
|
||||
if w.Tree {
|
||||
writeCommandTree(w, node)
|
||||
} else {
|
||||
iw := w.Indent()
|
||||
if w.Compact {
|
||||
writeCompactCommandList(cmds, iw)
|
||||
} else {
|
||||
writeCommandList(cmds, iw)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
iw := w.Indent()
|
||||
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(iw, rows)
|
||||
}
|
||||
|
||||
// nolint: unused
|
||||
type helpCommandGroup struct {
|
||||
Name string
|
||||
Commands []*Node
|
||||
}
|
||||
|
||||
// nolint: unused, deadcode
|
||||
func collectCommandGroups(nodes []*Node) []helpCommandGroup {
|
||||
groups := map[string][]*Node{}
|
||||
for _, node := range nodes {
|
||||
groups[node.Group] = append(groups[node.Group], node)
|
||||
}
|
||||
out := []helpCommandGroup{}
|
||||
for name, nodes := range groups {
|
||||
if name == "" {
|
||||
name = "Commands"
|
||||
}
|
||||
out = append(out, helpCommandGroup{Name: name, Commands: nodes})
|
||||
}
|
||||
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{}
|
||||
w := &helpWriter{
|
||||
indent: "",
|
||||
width: guessWidth(ctx.Stdout),
|
||||
lines: &lines,
|
||||
helpFormatter: ctx.Kong.helpFormatter,
|
||||
HelpOptions: options,
|
||||
}
|
||||
return w
|
||||
}
|
||||
|
||||
func (h *helpWriter) Printf(format string, args ...interface{}) {
|
||||
h.Print(fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
func (h *helpWriter) Print(text string) {
|
||||
*h.lines = append(*h.lines, strings.TrimRight(h.indent+text, " "))
|
||||
}
|
||||
|
||||
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)
|
||||
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)
|
||||
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()
|
||||
if flag.Short != 0 {
|
||||
flagString += fmt.Sprintf("-%c, --%s", flag.Short, name)
|
||||
} else {
|
||||
if haveShort {
|
||||
flagString += fmt.Sprintf(" --%s", name)
|
||||
} else {
|
||||
flagString += fmt.Sprintf("--%s", name)
|
||||
}
|
||||
}
|
||||
if !isBool {
|
||||
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
|
||||
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 {
|
||||
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
|
||||
}
|
||||
19
vendor/github.com/alecthomas/kong/hooks.go
generated
vendored
Normal file
19
vendor/github.com/alecthomas/kong/hooks.go
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
package kong
|
||||
|
||||
// BeforeResolve is a documentation-only interface describing hooks that run before values are set.
|
||||
type BeforeResolve interface {
|
||||
// This is not the correct signature - see README for details.
|
||||
BeforeResolve(args ...interface{}) 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 ...interface{}) 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 ...interface{}) error
|
||||
}
|
||||
39
vendor/github.com/alecthomas/kong/interpolate.go
generated
vendored
Normal file
39
vendor/github.com/alecthomas/kong/interpolate.go
generated
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
package kong
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
var interpolationRegex = regexp.MustCompile(`((?:\${([[:alpha:]_][[:word:]]*))(?:=([^}]+))?})|(\$)|([^$]+)`)
|
||||
|
||||
// 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 name := match[2]; name != "" {
|
||||
value, ok := vars[name]
|
||||
if !ok {
|
||||
// No default value.
|
||||
if match[3] == "" {
|
||||
return "", fmt.Errorf("undefined variable ${%s}", name)
|
||||
}
|
||||
value = match[3]
|
||||
}
|
||||
out += value
|
||||
} else {
|
||||
out += match[0]
|
||||
}
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
368
vendor/github.com/alecthomas/kong/kong.go
generated
vendored
Normal file
368
vendor/github.com/alecthomas/kong/kong.go
generated
vendored
Normal file
@@ -0,0 +1,368 @@
|
||||
package kong
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
callbackReturnSignature = reflect.TypeOf((*error)(nil)).Elem()
|
||||
)
|
||||
|
||||
// Error reported by Kong.
|
||||
type Error struct{ msg string }
|
||||
|
||||
func (e Error) Error() string { return e.msg }
|
||||
|
||||
func fail(format string, args ...interface{}) {
|
||||
panic(Error{msg: fmt.Sprintf(format, args...)})
|
||||
}
|
||||
|
||||
// Must creates a new Parser or panics if there is an error.
|
||||
func Must(ast interface{}, options ...Option) *Kong {
|
||||
k, err := New(ast, options...)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return k
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
noDefaultHelp bool
|
||||
usageOnError bool
|
||||
help HelpPrinter
|
||||
helpFormatter HelpValueFormatter
|
||||
helpOptions HelpOptions
|
||||
helpFlag *Flag
|
||||
vars Vars
|
||||
|
||||
// Set temporarily by Options. These are applied after build().
|
||||
postBuildOptions []Option
|
||||
}
|
||||
|
||||
// New creates a new Kong parser on grammar.
|
||||
//
|
||||
// See the README (https://github.com/alecthomas/kong) for usage instructions.
|
||||
func New(grammar interface{}, options ...Option) (*Kong, error) {
|
||||
k := &Kong{
|
||||
Exit: os.Exit,
|
||||
Stdout: os.Stdout,
|
||||
Stderr: os.Stderr,
|
||||
registry: NewRegistry().RegisterDefaults(),
|
||||
vars: Vars{},
|
||||
bindings: bindings{},
|
||||
helpFormatter: DefaultHelpValueFormatter,
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
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)
|
||||
|
||||
return k, 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 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)
|
||||
}
|
||||
value.Help, err = interpolate(value.Help, vars, map[string]string{
|
||||
"default": value.Default,
|
||||
"enum": value.Enum,
|
||||
})
|
||||
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 helpValue
|
||||
value := reflect.ValueOf(&helpTarget).Elem()
|
||||
helpFlag := &Flag{
|
||||
Short: 'h',
|
||||
Value: &Value{
|
||||
Name: "help",
|
||||
Help: "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) {
|
||||
defer catch(&err)
|
||||
ctx, err = Trace(k, args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ctx.Error != nil {
|
||||
return nil, &ParseError{error: ctx.Error, 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 {
|
||||
return nil, &ParseError{error: err, Context: ctx}
|
||||
}
|
||||
if err = ctx.Validate(); err != nil {
|
||||
return nil, &ParseError{error: err, Context: ctx}
|
||||
}
|
||||
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")
|
||||
}
|
||||
method := getMethod(value, name)
|
||||
if !method.IsValid() {
|
||||
continue
|
||||
}
|
||||
binds := k.bindings.clone()
|
||||
binds.add(ctx, trace)
|
||||
binds.add(trace.Node().Vars().CloneWith(k.vars))
|
||||
binds.merge(ctx.bindings)
|
||||
if err := callMethod(name, value, method, binds); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// Path[0] will always be the app root.
|
||||
return k.applyHookToDefaultFlags(ctx, ctx.Path[0].Node(), 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.Default == "" || ctx.values[flag.Value].IsValid() || !flag.Target.IsValid() {
|
||||
continue
|
||||
}
|
||||
method := getMethod(flag.Target, name)
|
||||
if !method.IsValid() {
|
||||
continue
|
||||
}
|
||||
path := &Path{Flag: flag}
|
||||
if err := callMethod(name, flag.Target, method, binds.clone().add(path)); err != nil {
|
||||
return next(err)
|
||||
}
|
||||
}
|
||||
return next(nil)
|
||||
})
|
||||
}
|
||||
|
||||
func formatMultilineMessage(w io.Writer, leaders []string, format string, args ...interface{}) {
|
||||
lines := strings.Split(fmt.Sprintf(format, args...), "\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 ...interface{}) *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 ...interface{}) *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 a non-zero status.
|
||||
func (k *Kong) Fatalf(format string, args ...interface{}) {
|
||||
k.Errorf(format, args...)
|
||||
k.Exit(1)
|
||||
}
|
||||
|
||||
// FatalIfErrorf terminates with an error message if err != nil.
|
||||
func (k *Kong) FatalIfErrorf(err error, args ...interface{}) {
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
msg := err.Error()
|
||||
if len(args) > 0 {
|
||||
msg = fmt.Sprintf(args[0].(string), args[1:]...) + ": " + err.Error()
|
||||
}
|
||||
// Maybe display usage information.
|
||||
if err, ok := err.(*ParseError); ok && k.usageOnError {
|
||||
options := k.helpOptions
|
||||
_ = k.help(options, err.Context)
|
||||
fmt.Fprintln(k.Stdout)
|
||||
}
|
||||
k.Errorf("%s", msg)
|
||||
k.Exit(1)
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
func catch(err *error) {
|
||||
msg := recover()
|
||||
if test, ok := msg.(Error); ok {
|
||||
*err = test
|
||||
} else if msg != nil {
|
||||
panic(msg)
|
||||
}
|
||||
}
|
||||
BIN
vendor/github.com/alecthomas/kong/kong.png
generated
vendored
Normal file
BIN
vendor/github.com/alecthomas/kong/kong.png
generated
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 67 KiB |
BIN
vendor/github.com/alecthomas/kong/kong.sketch
generated
vendored
Normal file
BIN
vendor/github.com/alecthomas/kong/kong.sketch
generated
vendored
Normal file
Binary file not shown.
39
vendor/github.com/alecthomas/kong/levenshtein.go
generated
vendored
Normal file
39
vendor/github.com/alecthomas/kong/levenshtein.go
generated
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
package kong
|
||||
|
||||
import "unicode/utf8"
|
||||
|
||||
// https://en.wikibooks.org/wiki/Algorithm_Implementation/Strings/Levenshtein_distance#Go
|
||||
// License: https://creativecommons.org/licenses/by-sa/3.0/
|
||||
func levenshtein(a, b string) int {
|
||||
f := make([]int, utf8.RuneCountInString(b)+1)
|
||||
|
||||
for j := range f {
|
||||
f[j] = j
|
||||
}
|
||||
|
||||
for _, ca := range a {
|
||||
j := 1
|
||||
fj1 := f[0] // fj1 is the value of f[j - 1] in last iteration
|
||||
f[0]++
|
||||
for _, cb := range b {
|
||||
mn := min(f[j]+1, f[j-1]+1) // delete & insert
|
||||
if cb != ca {
|
||||
mn = min(mn, fj1+1) // change
|
||||
} else {
|
||||
mn = min(mn, fj1) // matched
|
||||
}
|
||||
|
||||
fj1, f[j] = f[j], mn // save f[j] to fj1(j is about to increase), update f[j] to mn
|
||||
j++
|
||||
}
|
||||
}
|
||||
|
||||
return f[len(f)-1]
|
||||
}
|
||||
|
||||
func min(a, b int) int {
|
||||
if a <= b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
727
vendor/github.com/alecthomas/kong/mapper.go
generated
vendored
Normal file
727
vendor/github.com/alecthomas/kong/mapper.go
generated
vendored
Normal file
@@ -0,0 +1,727 @@
|
||||
package kong
|
||||
|
||||
import (
|
||||
"encoding"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"math/bits"
|
||||
"net/url"
|
||||
"os"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
var (
|
||||
mapperValueType = reflect.TypeOf((*MapperValue)(nil)).Elem()
|
||||
boolMapperType = reflect.TypeOf((*BoolMapper)(nil)).Elem()
|
||||
textUnmarshalerType = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem()
|
||||
binaryUnmarshalerType = reflect.TypeOf((*encoding.BinaryUnmarshaler)(nil)).Elem()
|
||||
)
|
||||
|
||||
// DecodeContext is passed to a Mapper's Decode().
|
||||
//
|
||||
// It contains the Value being decoded into and the Scanner to parse from.
|
||||
type DecodeContext struct {
|
||||
// Value being decoded into.
|
||||
Value *Value
|
||||
// Scan contains the input to scan into Target.
|
||||
Scan *Scanner
|
||||
}
|
||||
|
||||
// WithScanner creates a clone of this context with a new Scanner.
|
||||
func (r *DecodeContext) WithScanner(scan *Scanner) *DecodeContext {
|
||||
return &DecodeContext{
|
||||
Value: r.Value,
|
||||
Scan: scan,
|
||||
}
|
||||
}
|
||||
|
||||
// MapperValue may be implemented by fields in order to provide custom mapping.
|
||||
type MapperValue interface {
|
||||
Decode(ctx *DecodeContext) error
|
||||
}
|
||||
|
||||
type mapperValueAdapter struct {
|
||||
isBool bool
|
||||
}
|
||||
|
||||
func (m *mapperValueAdapter) Decode(ctx *DecodeContext, target reflect.Value) error {
|
||||
if target.Type().Implements(mapperValueType) {
|
||||
return target.Interface().(MapperValue).Decode(ctx)
|
||||
}
|
||||
return target.Addr().Interface().(MapperValue).Decode(ctx)
|
||||
}
|
||||
|
||||
func (m *mapperValueAdapter) IsBool() bool {
|
||||
return m.isBool
|
||||
}
|
||||
|
||||
type textUnmarshalerAdapter struct{}
|
||||
|
||||
func (m *textUnmarshalerAdapter) Decode(ctx *DecodeContext, target reflect.Value) error {
|
||||
var value string
|
||||
err := ctx.Scan.PopValueInto("value", &value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if target.Type().Implements(textUnmarshalerType) {
|
||||
return target.Interface().(encoding.TextUnmarshaler).UnmarshalText([]byte(value))
|
||||
}
|
||||
return target.Addr().Interface().(encoding.TextUnmarshaler).UnmarshalText([]byte(value))
|
||||
}
|
||||
|
||||
type binaryUnmarshalerAdapter struct{}
|
||||
|
||||
func (m *binaryUnmarshalerAdapter) Decode(ctx *DecodeContext, target reflect.Value) error {
|
||||
var value string
|
||||
err := ctx.Scan.PopValueInto("value", &value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if target.Type().Implements(textUnmarshalerType) {
|
||||
return target.Interface().(encoding.BinaryUnmarshaler).UnmarshalBinary([]byte(value))
|
||||
}
|
||||
return target.Addr().Interface().(encoding.BinaryUnmarshaler).UnmarshalBinary([]byte(value))
|
||||
}
|
||||
|
||||
// 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 MappverValue interface, it will be used.
|
||||
type Mapper interface {
|
||||
// Decode ctx.Value with ctx.Scanner into target.
|
||||
Decode(ctx *DecodeContext, target reflect.Value) error
|
||||
}
|
||||
|
||||
// A BoolMapper is a Mapper to a value that is a boolean.
|
||||
//
|
||||
// This is used solely for formatting help.
|
||||
type BoolMapper interface {
|
||||
IsBool() bool
|
||||
}
|
||||
|
||||
// A MapperFunc is a single function that complies with the Mapper interface.
|
||||
type MapperFunc func(ctx *DecodeContext, target reflect.Value) error
|
||||
|
||||
func (m MapperFunc) Decode(ctx *DecodeContext, target reflect.Value) error { // nolint: golint
|
||||
return m(ctx, target)
|
||||
}
|
||||
|
||||
// A Registry contains a set of mappers and supporting lookup methods.
|
||||
type Registry struct {
|
||||
names map[string]Mapper
|
||||
types map[reflect.Type]Mapper
|
||||
kinds map[reflect.Kind]Mapper
|
||||
values map[reflect.Value]Mapper
|
||||
}
|
||||
|
||||
// NewRegistry creates a new (empty) Registry.
|
||||
func NewRegistry() *Registry {
|
||||
return &Registry{
|
||||
names: map[string]Mapper{},
|
||||
types: map[reflect.Type]Mapper{},
|
||||
kinds: map[reflect.Kind]Mapper{},
|
||||
values: map[reflect.Value]Mapper{},
|
||||
}
|
||||
}
|
||||
|
||||
// ForNamedValue finds a mapper for a value with a user-specified name.
|
||||
//
|
||||
// Will return nil if a mapper can not be determined.
|
||||
func (r *Registry) ForNamedValue(name string, value reflect.Value) Mapper {
|
||||
if mapper, ok := r.names[name]; ok {
|
||||
return mapper
|
||||
}
|
||||
return r.ForValue(value)
|
||||
}
|
||||
|
||||
// ForValue looks up the Mapper for a reflect.Value.
|
||||
func (r *Registry) ForValue(value reflect.Value) Mapper {
|
||||
if mapper, ok := r.values[value]; ok {
|
||||
return mapper
|
||||
}
|
||||
return r.ForType(value.Type())
|
||||
}
|
||||
|
||||
// ForNamedType finds a mapper for a type with a user-specified name.
|
||||
//
|
||||
// Will return nil if a mapper can not be determined.
|
||||
func (r *Registry) ForNamedType(name string, typ reflect.Type) Mapper {
|
||||
if mapper, ok := r.names[name]; ok {
|
||||
return mapper
|
||||
}
|
||||
return r.ForType(typ)
|
||||
}
|
||||
|
||||
// ForType finds a mapper from a type, by type, then kind.
|
||||
//
|
||||
// Will return nil if a mapper can not be determined.
|
||||
func (r *Registry) ForType(typ reflect.Type) Mapper {
|
||||
// Check if the type implements MapperValue.
|
||||
for _, impl := range []reflect.Type{typ, reflect.PtrTo(typ)} {
|
||||
if impl.Implements(mapperValueType) {
|
||||
return &mapperValueAdapter{impl.Implements(boolMapperType)}
|
||||
}
|
||||
}
|
||||
// Next, try explicitly registered types.
|
||||
var mapper Mapper
|
||||
var ok bool
|
||||
if mapper, ok = r.types[typ]; ok {
|
||||
return mapper
|
||||
}
|
||||
// Next try stdlib unmarshaler interfaces.
|
||||
for _, impl := range []reflect.Type{typ, reflect.PtrTo(typ)} {
|
||||
if impl.Implements(textUnmarshalerType) {
|
||||
return &textUnmarshalerAdapter{}
|
||||
} else if impl.Implements(binaryUnmarshalerType) {
|
||||
return &binaryUnmarshalerAdapter{}
|
||||
}
|
||||
}
|
||||
// Finally try registered kinds.
|
||||
if mapper, ok = r.kinds[typ.Kind()]; ok {
|
||||
return mapper
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RegisterKind registers a Mapper for a reflect.Kind.
|
||||
func (r *Registry) RegisterKind(kind reflect.Kind, mapper Mapper) *Registry {
|
||||
r.kinds[kind] = mapper
|
||||
return r
|
||||
}
|
||||
|
||||
// RegisterName registers a mapper to be used if the value mapper has a "type" tag matching name.
|
||||
//
|
||||
// eg.
|
||||
//
|
||||
// Mapper string `kong:"type='colour'`
|
||||
// registry.RegisterName("colour", ...)
|
||||
func (r *Registry) RegisterName(name string, mapper Mapper) *Registry {
|
||||
r.names[name] = mapper
|
||||
return r
|
||||
}
|
||||
|
||||
// RegisterType registers a Mapper for a reflect.Type.
|
||||
func (r *Registry) RegisterType(typ reflect.Type, mapper Mapper) *Registry {
|
||||
r.types[typ] = mapper
|
||||
return r
|
||||
}
|
||||
|
||||
// RegisterValue registers a Mapper by pointer to the field value.
|
||||
func (r *Registry) RegisterValue(ptr interface{}, mapper Mapper) *Registry {
|
||||
key := reflect.ValueOf(ptr)
|
||||
if key.Kind() != reflect.Ptr {
|
||||
panic("expected a pointer")
|
||||
}
|
||||
key = key.Elem()
|
||||
r.values[key] = mapper
|
||||
return r
|
||||
}
|
||||
|
||||
// RegisterDefaults registers Mappers for all builtin supported Go types and some common stdlib types.
|
||||
func (r *Registry) RegisterDefaults() *Registry {
|
||||
return r.RegisterKind(reflect.Int, intDecoder(bits.UintSize)).
|
||||
RegisterKind(reflect.Int8, intDecoder(8)).
|
||||
RegisterKind(reflect.Int16, intDecoder(16)).
|
||||
RegisterKind(reflect.Int32, intDecoder(32)).
|
||||
RegisterKind(reflect.Int64, intDecoder(64)).
|
||||
RegisterKind(reflect.Uint, uintDecoder(64)).
|
||||
RegisterKind(reflect.Uint8, uintDecoder(bits.UintSize)).
|
||||
RegisterKind(reflect.Uint16, uintDecoder(16)).
|
||||
RegisterKind(reflect.Uint32, uintDecoder(32)).
|
||||
RegisterKind(reflect.Uint64, uintDecoder(64)).
|
||||
RegisterKind(reflect.Float32, floatDecoder(32)).
|
||||
RegisterKind(reflect.Float64, floatDecoder(64)).
|
||||
RegisterKind(reflect.String, MapperFunc(func(ctx *DecodeContext, target reflect.Value) error {
|
||||
err := ctx.Scan.PopValueInto("string", target.Addr().Interface())
|
||||
return err
|
||||
})).
|
||||
RegisterKind(reflect.Bool, boolMapper{}).
|
||||
RegisterKind(reflect.Slice, sliceDecoder(r)).
|
||||
RegisterKind(reflect.Map, mapDecoder(r)).
|
||||
RegisterType(reflect.TypeOf(time.Time{}), timeDecoder()).
|
||||
RegisterType(reflect.TypeOf(time.Duration(0)), durationDecoder()).
|
||||
RegisterType(reflect.TypeOf(&url.URL{}), urlMapper()).
|
||||
RegisterName("path", pathMapper(r)).
|
||||
RegisterName("existingfile", existingFileMapper(r)).
|
||||
RegisterName("existingdir", existingDirMapper(r)).
|
||||
RegisterName("counter", counterMapper())
|
||||
}
|
||||
|
||||
type boolMapper struct{}
|
||||
|
||||
func (boolMapper) Decode(ctx *DecodeContext, target reflect.Value) error {
|
||||
if ctx.Scan.Peek().Type == FlagValueToken {
|
||||
token := ctx.Scan.Pop()
|
||||
switch v := token.Value.(type) {
|
||||
case string:
|
||||
v = strings.ToLower(v)
|
||||
switch v {
|
||||
case "true", "1", "yes":
|
||||
target.SetBool(true)
|
||||
|
||||
case "false", "0", "no":
|
||||
target.SetBool(false)
|
||||
|
||||
default:
|
||||
return errors.Errorf("bool value must be true, 1, yes, false, 0 or no but got %q", v)
|
||||
}
|
||||
|
||||
case bool:
|
||||
target.SetBool(v)
|
||||
|
||||
default:
|
||||
return errors.Errorf("expected bool but got %q (%T)", token.Value, token.Value)
|
||||
}
|
||||
} else {
|
||||
target.SetBool(true)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (boolMapper) IsBool() bool { return true }
|
||||
|
||||
func durationDecoder() MapperFunc {
|
||||
return func(ctx *DecodeContext, target reflect.Value) error {
|
||||
var value string
|
||||
if err := ctx.Scan.PopValueInto("duration", &value); err != nil {
|
||||
return err
|
||||
}
|
||||
r, err := time.ParseDuration(value)
|
||||
if err != nil {
|
||||
return errors.Errorf("expected duration but got %q: %s", value, err)
|
||||
}
|
||||
target.Set(reflect.ValueOf(r))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func timeDecoder() MapperFunc {
|
||||
return func(ctx *DecodeContext, target reflect.Value) error {
|
||||
format := time.RFC3339
|
||||
if ctx.Value.Format != "" {
|
||||
format = ctx.Value.Format
|
||||
}
|
||||
var value string
|
||||
if err := ctx.Scan.PopValueInto("time", &value); err != nil {
|
||||
return err
|
||||
}
|
||||
t, err := time.Parse(format, value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
target.Set(reflect.ValueOf(t))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func intDecoder(bits int) MapperFunc { // nolint: dupl
|
||||
return func(ctx *DecodeContext, target reflect.Value) error {
|
||||
t, err := ctx.Scan.PopValue("int")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var sv string
|
||||
switch v := t.Value.(type) {
|
||||
case string:
|
||||
sv = v
|
||||
|
||||
case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float32, float64:
|
||||
sv = fmt.Sprintf("%v", v)
|
||||
|
||||
default:
|
||||
return errors.Errorf("expected an int but got %q (%T)", t, t.Value)
|
||||
}
|
||||
n, err := strconv.ParseInt(sv, 10, bits)
|
||||
if err != nil {
|
||||
return errors.Errorf("expected a valid %d bit int but got %q", bits, sv)
|
||||
}
|
||||
target.SetInt(n)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func uintDecoder(bits int) MapperFunc { // nolint: dupl
|
||||
return func(ctx *DecodeContext, target reflect.Value) error {
|
||||
t, err := ctx.Scan.PopValue("uint")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var sv string
|
||||
switch v := t.Value.(type) {
|
||||
case string:
|
||||
sv = v
|
||||
|
||||
case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float32, float64:
|
||||
sv = fmt.Sprintf("%v", v)
|
||||
|
||||
default:
|
||||
return errors.Errorf("expected an int but got %q (%T)", t, t.Value)
|
||||
}
|
||||
n, err := strconv.ParseUint(sv, 10, bits)
|
||||
if err != nil {
|
||||
return errors.Errorf("expected a valid %d bit uint but got %q", bits, sv)
|
||||
}
|
||||
target.SetUint(n)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func floatDecoder(bits int) MapperFunc {
|
||||
return func(ctx *DecodeContext, target reflect.Value) error {
|
||||
t, err := ctx.Scan.PopValue("float")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch v := t.Value.(type) {
|
||||
case string:
|
||||
n, err := strconv.ParseFloat(v, bits)
|
||||
if err != nil {
|
||||
return errors.Errorf("expected a float but got %q (%T)", t, t.Value)
|
||||
}
|
||||
target.SetFloat(n)
|
||||
|
||||
case float32:
|
||||
target.SetFloat(float64(v))
|
||||
|
||||
case float64:
|
||||
target.SetFloat(v)
|
||||
|
||||
case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
|
||||
target.Set(reflect.ValueOf(v))
|
||||
|
||||
default:
|
||||
return errors.Errorf("expected an int but got %q (%T)", t, t.Value)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func mapDecoder(r *Registry) MapperFunc {
|
||||
return func(ctx *DecodeContext, target reflect.Value) error {
|
||||
if target.IsNil() {
|
||||
target.Set(reflect.MakeMap(target.Type()))
|
||||
}
|
||||
el := target.Type()
|
||||
sep := ctx.Value.Tag.MapSep
|
||||
var childScanner *Scanner
|
||||
if ctx.Value.Flag != nil {
|
||||
t := ctx.Scan.Pop()
|
||||
// If decoding a flag, we need an argument.
|
||||
if t.IsEOL() {
|
||||
return errors.Errorf("unexpected EOL")
|
||||
}
|
||||
switch v := t.Value.(type) {
|
||||
case string:
|
||||
childScanner = Scan(SplitEscaped(v, sep)...)
|
||||
|
||||
case []map[string]interface{}:
|
||||
for _, m := range v {
|
||||
err := jsonTranscode(m, target.Addr().Interface())
|
||||
if err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
case map[string]interface{}:
|
||||
return jsonTranscode(v, target.Addr().Interface())
|
||||
|
||||
default:
|
||||
return errors.Errorf("invalid map value %q (of type %T)", t, t.Value)
|
||||
}
|
||||
} else {
|
||||
tokens := ctx.Scan.PopWhile(func(t Token) bool { return t.IsValue() })
|
||||
childScanner = ScanFromTokens(tokens...)
|
||||
}
|
||||
for !childScanner.Peek().IsEOL() {
|
||||
var token string
|
||||
err := childScanner.PopValueInto("map", &token)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
parts := strings.SplitN(token, "=", 2)
|
||||
if len(parts) != 2 {
|
||||
return errors.Errorf("expected \"<key>=<value>\" but got %q", token)
|
||||
}
|
||||
key, value := parts[0], parts[1]
|
||||
|
||||
keyTypeName, valueTypeName := "", ""
|
||||
if typ := ctx.Value.Tag.Type; typ != "" {
|
||||
parts := strings.Split(typ, ":")
|
||||
if len(parts) != 2 {
|
||||
return errors.Errorf("type:\"\" on map field must be in the form \"[<keytype>]:[<valuetype>]\"")
|
||||
}
|
||||
keyTypeName, valueTypeName = parts[0], parts[1]
|
||||
}
|
||||
|
||||
keyScanner := Scan(key)
|
||||
keyDecoder := r.ForNamedType(keyTypeName, el.Key())
|
||||
keyValue := reflect.New(el.Key()).Elem()
|
||||
if err := keyDecoder.Decode(ctx.WithScanner(keyScanner), keyValue); err != nil {
|
||||
return errors.Errorf("invalid map key %q", key)
|
||||
}
|
||||
|
||||
valueScanner := Scan(value)
|
||||
valueDecoder := r.ForNamedType(valueTypeName, el.Elem())
|
||||
valueValue := reflect.New(el.Elem()).Elem()
|
||||
if err := valueDecoder.Decode(ctx.WithScanner(valueScanner), valueValue); err != nil {
|
||||
return errors.Errorf("invalid map value %q", value)
|
||||
}
|
||||
|
||||
target.SetMapIndex(keyValue, valueValue)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func sliceDecoder(r *Registry) MapperFunc {
|
||||
return func(ctx *DecodeContext, target reflect.Value) error {
|
||||
el := target.Type().Elem()
|
||||
sep := ctx.Value.Tag.Sep
|
||||
var childScanner *Scanner
|
||||
if ctx.Value.Flag != nil {
|
||||
t := ctx.Scan.Pop()
|
||||
// If decoding a flag, we need an argument.
|
||||
if t.IsEOL() {
|
||||
return errors.Errorf("unexpected EOL")
|
||||
}
|
||||
switch v := t.Value.(type) {
|
||||
case string:
|
||||
childScanner = Scan(SplitEscaped(v, sep)...)
|
||||
|
||||
case []interface{}:
|
||||
return jsonTranscode(v, target.Addr().Interface())
|
||||
|
||||
default:
|
||||
v = []interface{}{v}
|
||||
return jsonTranscode(v, target.Addr().Interface())
|
||||
}
|
||||
} else {
|
||||
tokens := ctx.Scan.PopWhile(func(t Token) bool { return t.IsValue() })
|
||||
childScanner = ScanFromTokens(tokens...)
|
||||
}
|
||||
childDecoder := r.ForNamedType(ctx.Value.Tag.Type, el)
|
||||
if childDecoder == nil {
|
||||
return errors.Errorf("no mapper for element type of %s", target.Type())
|
||||
}
|
||||
for !childScanner.Peek().IsEOL() {
|
||||
childValue := reflect.New(el).Elem()
|
||||
err := childDecoder.Decode(ctx.WithScanner(childScanner), childValue)
|
||||
if err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
target.Set(reflect.Append(target, childValue))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func pathMapper(r *Registry) MapperFunc {
|
||||
return func(ctx *DecodeContext, target reflect.Value) error {
|
||||
if target.Kind() == reflect.Slice {
|
||||
return sliceDecoder(r)(ctx, target)
|
||||
}
|
||||
if target.Kind() != reflect.String {
|
||||
return errors.Errorf("\"path\" type must be applied to a string not %s", target.Type())
|
||||
}
|
||||
var path string
|
||||
err := ctx.Scan.PopValueInto("file", &path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
path = ExpandPath(path)
|
||||
target.SetString(path)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func existingFileMapper(r *Registry) MapperFunc {
|
||||
return func(ctx *DecodeContext, target reflect.Value) error {
|
||||
if target.Kind() == reflect.Slice {
|
||||
return sliceDecoder(r)(ctx, target)
|
||||
}
|
||||
if target.Kind() != reflect.String {
|
||||
return errors.Errorf("\"existingfile\" type must be applied to a string not %s", target.Type())
|
||||
}
|
||||
var path string
|
||||
err := ctx.Scan.PopValueInto("file", &path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if path != "-" {
|
||||
path = ExpandPath(path)
|
||||
stat, err := os.Stat(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if stat.IsDir() {
|
||||
return errors.Errorf("%q exists but is a directory", path)
|
||||
}
|
||||
}
|
||||
target.SetString(path)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func existingDirMapper(r *Registry) MapperFunc {
|
||||
return func(ctx *DecodeContext, target reflect.Value) error {
|
||||
if target.Kind() == reflect.Slice {
|
||||
return sliceDecoder(r)(ctx, target)
|
||||
}
|
||||
if target.Kind() != reflect.String {
|
||||
return errors.Errorf("\"existingdir\" must be applied to a string not %s", target.Type())
|
||||
}
|
||||
var path string
|
||||
err := ctx.Scan.PopValueInto("file", &path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
path = ExpandPath(path)
|
||||
stat, err := os.Stat(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !stat.IsDir() {
|
||||
return errors.Errorf("%q exists but is not a directory", path)
|
||||
}
|
||||
target.SetString(path)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func counterMapper() MapperFunc {
|
||||
return func(ctx *DecodeContext, target reflect.Value) error {
|
||||
if ctx.Scan.Peek().Type == FlagValueToken {
|
||||
t, err := ctx.Scan.PopValue("counter")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch v := t.Value.(type) {
|
||||
case string:
|
||||
n, err := strconv.ParseInt(v, 10, 64)
|
||||
if err != nil {
|
||||
return errors.Errorf("expected a counter but got %q (%T)", t, t.Value)
|
||||
}
|
||||
target.SetInt(n)
|
||||
|
||||
case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
|
||||
target.Set(reflect.ValueOf(v))
|
||||
|
||||
default:
|
||||
return errors.Errorf("expected a counter but got %q (%T)", t, t.Value)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
switch target.Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
target.SetInt(target.Int() + 1)
|
||||
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
target.SetUint(target.Uint() + 1)
|
||||
|
||||
case reflect.Float32, reflect.Float64:
|
||||
target.SetFloat(target.Float() + 1)
|
||||
|
||||
default:
|
||||
return errors.Errorf("type:\"counter\" must be used with a numeric field")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func urlMapper() MapperFunc {
|
||||
return func(ctx *DecodeContext, target reflect.Value) error {
|
||||
var urlStr string
|
||||
err := ctx.Scan.PopValueInto("url", &urlStr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
url, err := url.Parse(urlStr)
|
||||
if err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
target.Set(reflect.ValueOf(url))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// SplitEscaped splits a string on a separator.
|
||||
//
|
||||
// It differs from strings.Split() in that the separator can exist in a field by escaping it with a \. eg.
|
||||
//
|
||||
// SplitEscaped(`hello\,there,bob`, ',') == []string{"hello,there", "bob"}
|
||||
func SplitEscaped(s string, sep rune) (out []string) {
|
||||
if sep == -1 {
|
||||
return []string{s}
|
||||
}
|
||||
escaped := false
|
||||
token := ""
|
||||
for _, ch := range s {
|
||||
switch {
|
||||
case escaped:
|
||||
token += string(ch)
|
||||
escaped = false
|
||||
case ch == '\\':
|
||||
escaped = true
|
||||
case ch == sep && !escaped:
|
||||
out = append(out, token)
|
||||
token = ""
|
||||
escaped = false
|
||||
default:
|
||||
token += string(ch)
|
||||
}
|
||||
}
|
||||
if token != "" {
|
||||
out = append(out, token)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// JoinEscaped joins a slice of strings on sep, but also escapes any instances of sep in the fields with \. eg.
|
||||
//
|
||||
// JoinEscaped([]string{"hello,there", "bob"}, ',') == `hello\,there,bob`
|
||||
func JoinEscaped(s []string, sep rune) string {
|
||||
escaped := []string{}
|
||||
for _, e := range s {
|
||||
escaped = append(escaped, strings.Replace(e, string(sep), `\`+string(sep), -1))
|
||||
}
|
||||
return strings.Join(escaped, string(sep))
|
||||
}
|
||||
|
||||
// FileContentFlag is a flag value that loads a file's contents into its value.
|
||||
type FileContentFlag []byte
|
||||
|
||||
func (f *FileContentFlag) Decode(ctx *DecodeContext) error { // nolint: golint
|
||||
var filename string
|
||||
err := ctx.Scan.PopValueInto("filename", &filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// This allows unsetting of file content flags.
|
||||
if filename == "" {
|
||||
*f = nil
|
||||
return nil
|
||||
}
|
||||
filename = ExpandPath(filename)
|
||||
data, err := ioutil.ReadFile(filename) // nolint: gosec
|
||||
if err != nil {
|
||||
return errors.Errorf("failed to open %q: %s", filename, err)
|
||||
}
|
||||
*f = data
|
||||
return nil
|
||||
}
|
||||
|
||||
func jsonTranscode(in, out interface{}) error {
|
||||
data, err := json.Marshal(in)
|
||||
if err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
return errors.Wrapf(json.Unmarshal(data, out), "%#v -> %T", in, out)
|
||||
}
|
||||
433
vendor/github.com/alecthomas/kong/model.go
generated
vendored
Normal file
433
vendor/github.com/alecthomas/kong/model.go
generated
vendored
Normal file
@@ -0,0 +1,433 @@
|
||||
package kong
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"os"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// A Visitable component in the model.
|
||||
type Visitable interface {
|
||||
node()
|
||||
}
|
||||
|
||||
// Application is the root of the Kong model.
|
||||
type Application struct {
|
||||
*Node
|
||||
// Help flag, if the NoDefaultHelp() option is not specified.
|
||||
HelpFlag *Flag
|
||||
}
|
||||
|
||||
// Argument represents a branching positional argument.
|
||||
type Argument = Node
|
||||
|
||||
// Command represents a command in the CLI.
|
||||
type Command = Node
|
||||
|
||||
// NodeType is an enum representing the type of a Node.
|
||||
type NodeType int
|
||||
|
||||
// Node type enumerations.
|
||||
const (
|
||||
ApplicationNode NodeType = iota
|
||||
CommandNode
|
||||
ArgumentNode
|
||||
)
|
||||
|
||||
// Node is a branch in the CLI. ie. a command or positional argument.
|
||||
type Node struct {
|
||||
Type NodeType
|
||||
Parent *Node
|
||||
Name string
|
||||
Help string // Short help displayed in summaries.
|
||||
Detail string // Detailed help displayed when describing command/arg alone.
|
||||
Group string
|
||||
Hidden bool
|
||||
Flags []*Flag
|
||||
Positional []*Positional
|
||||
Children []*Node
|
||||
Target reflect.Value // Pointer to the value in the grammar that this Node is associated with.
|
||||
Tag *Tag
|
||||
|
||||
Argument *Value // Populated when Type is ArgumentNode.
|
||||
}
|
||||
|
||||
func (*Node) node() {}
|
||||
|
||||
// Leaf returns true if this Node is a leaf node.
|
||||
func (n *Node) Leaf() bool {
|
||||
return len(n.Children) == 0
|
||||
}
|
||||
|
||||
// Find a command/argument/flag by pointer to its field.
|
||||
//
|
||||
// Returns nil if not found. Panics if ptr is not a pointer.
|
||||
func (n *Node) Find(ptr interface{}) *Node {
|
||||
key := reflect.ValueOf(ptr)
|
||||
if key.Kind() != reflect.Ptr {
|
||||
panic("expected a pointer")
|
||||
}
|
||||
return n.findNode(key)
|
||||
}
|
||||
|
||||
func (n *Node) findNode(key reflect.Value) *Node {
|
||||
if n.Target == key {
|
||||
return n
|
||||
}
|
||||
for _, child := range n.Children {
|
||||
if found := child.findNode(key); found != nil {
|
||||
return found
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// AllFlags returns flags from all ancestor branches encountered.
|
||||
//
|
||||
// If "hide" is true hidden flags will be omitted.
|
||||
func (n *Node) AllFlags(hide bool) (out [][]*Flag) {
|
||||
if n.Parent != nil {
|
||||
out = append(out, n.Parent.AllFlags(hide)...)
|
||||
}
|
||||
group := []*Flag{}
|
||||
for _, flag := range n.Flags {
|
||||
if !hide || !flag.Hidden {
|
||||
group = append(group, flag)
|
||||
}
|
||||
}
|
||||
if len(group) > 0 {
|
||||
out = append(out, group)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Leaves returns the leaf commands/arguments under Node.
|
||||
//
|
||||
// If "hidden" is true hidden leaves will be omitted.
|
||||
func (n *Node) Leaves(hide bool) (out []*Node) {
|
||||
_ = Visit(n, func(nd Visitable, next Next) error {
|
||||
if nd == n {
|
||||
return next(nil)
|
||||
}
|
||||
if node, ok := nd.(*Node); ok {
|
||||
if hide && node.Hidden {
|
||||
return nil
|
||||
}
|
||||
if len(node.Children) == 0 && node.Type != ApplicationNode {
|
||||
out = append(out, node)
|
||||
}
|
||||
}
|
||||
return next(nil)
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Depth of the command from the application root.
|
||||
func (n *Node) Depth() int {
|
||||
depth := 0
|
||||
p := n.Parent
|
||||
for p != nil && p.Type != ApplicationNode {
|
||||
depth++
|
||||
p = p.Parent
|
||||
}
|
||||
return depth
|
||||
}
|
||||
|
||||
// Summary help string for the node (not including application name).
|
||||
func (n *Node) Summary() string {
|
||||
summary := n.Path()
|
||||
if flags := n.FlagSummary(true); flags != "" {
|
||||
summary += " " + flags
|
||||
}
|
||||
args := []string{}
|
||||
for _, arg := range n.Positional {
|
||||
args = append(args, arg.Summary())
|
||||
}
|
||||
if len(args) != 0 {
|
||||
summary += " " + strings.Join(args, " ")
|
||||
} else if len(n.Children) > 0 {
|
||||
summary += " <command>"
|
||||
}
|
||||
return summary
|
||||
}
|
||||
|
||||
// FlagSummary for the node.
|
||||
func (n *Node) FlagSummary(hide bool) string {
|
||||
required := []string{}
|
||||
count := 0
|
||||
for _, group := range n.AllFlags(hide) {
|
||||
for _, flag := range group {
|
||||
count++
|
||||
if flag.Required {
|
||||
required = append(required, flag.Summary())
|
||||
}
|
||||
}
|
||||
}
|
||||
return strings.Join(required, " ")
|
||||
}
|
||||
|
||||
// FullPath is like Path() but includes the Application root node.
|
||||
func (n *Node) FullPath() string {
|
||||
root := n
|
||||
for root.Parent != nil {
|
||||
root = root.Parent
|
||||
}
|
||||
return strings.TrimSpace(root.Name + " " + n.Path())
|
||||
}
|
||||
|
||||
// Vars returns the combined Vars defined by all ancestors of this Node.
|
||||
func (n *Node) Vars() Vars {
|
||||
if n == nil {
|
||||
return Vars{}
|
||||
}
|
||||
return n.Parent.Vars().CloneWith(n.Tag.Vars)
|
||||
}
|
||||
|
||||
// Path through ancestors to this Node.
|
||||
func (n *Node) Path() (out string) {
|
||||
if n.Parent != nil {
|
||||
out += " " + n.Parent.Path()
|
||||
}
|
||||
switch n.Type {
|
||||
case CommandNode:
|
||||
out += " " + n.Name
|
||||
case ArgumentNode:
|
||||
out += " " + "<" + n.Name + ">"
|
||||
}
|
||||
return strings.TrimSpace(out)
|
||||
}
|
||||
|
||||
// A Value is either a flag or a variable positional argument.
|
||||
type Value struct {
|
||||
Flag *Flag // Nil if positional argument.
|
||||
Name string
|
||||
Help string
|
||||
Default string
|
||||
DefaultValue reflect.Value
|
||||
Enum string
|
||||
Mapper Mapper
|
||||
Tag *Tag
|
||||
Target reflect.Value
|
||||
Required bool
|
||||
Set bool // Set to true when this value is set through some mechanism.
|
||||
Format string // Formatting directive, if applicable.
|
||||
Position int // Position (for positional arguments).
|
||||
}
|
||||
|
||||
// EnumMap returns a map of the enums in this value.
|
||||
func (v *Value) EnumMap() map[string]bool {
|
||||
parts := strings.Split(v.Enum, ",")
|
||||
out := make(map[string]bool, len(parts))
|
||||
for _, part := range parts {
|
||||
out[strings.TrimSpace(part)] = true
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// ShortSummary returns a human-readable summary of the value, not including any placeholders/defaults.
|
||||
func (v *Value) ShortSummary() string {
|
||||
if v.Flag != nil {
|
||||
return fmt.Sprintf("--%s", v.Name)
|
||||
}
|
||||
argText := "<" + v.Name + ">"
|
||||
if v.IsCumulative() {
|
||||
argText += " ..."
|
||||
}
|
||||
if !v.Required {
|
||||
argText = "[" + argText + "]"
|
||||
}
|
||||
return argText
|
||||
}
|
||||
|
||||
// Summary returns a human-readable summary of the value.
|
||||
func (v *Value) Summary() string {
|
||||
if v.Flag != nil {
|
||||
if v.IsBool() {
|
||||
return fmt.Sprintf("--%s", v.Name)
|
||||
}
|
||||
return fmt.Sprintf("--%s=%s", v.Name, v.Flag.FormatPlaceHolder())
|
||||
}
|
||||
argText := "<" + v.Name + ">"
|
||||
if v.IsCumulative() {
|
||||
argText += " ..."
|
||||
}
|
||||
if !v.Required {
|
||||
argText = "[" + argText + "]"
|
||||
}
|
||||
return argText
|
||||
}
|
||||
|
||||
// IsCumulative returns true if the type can be accumulated into.
|
||||
func (v *Value) IsCumulative() bool {
|
||||
return v.IsSlice() || v.IsMap()
|
||||
}
|
||||
|
||||
// IsSlice returns true if the value is a slice.
|
||||
func (v *Value) IsSlice() bool {
|
||||
return v.Target.Type().Name() == "" && v.Target.Kind() == reflect.Slice
|
||||
}
|
||||
|
||||
// IsMap returns true if the value is a map.
|
||||
func (v *Value) IsMap() bool {
|
||||
return v.Target.Kind() == reflect.Map
|
||||
}
|
||||
|
||||
// IsBool returns true if the underlying value is a boolean.
|
||||
func (v *Value) IsBool() bool {
|
||||
if m, ok := v.Mapper.(BoolMapper); ok && m.IsBool() {
|
||||
return true
|
||||
}
|
||||
return v.Target.Kind() == reflect.Bool
|
||||
}
|
||||
|
||||
// Parse tokens into value, parse, and validate, but do not write to the field.
|
||||
func (v *Value) Parse(scan *Scanner, target reflect.Value) (err error) {
|
||||
defer func() {
|
||||
if rerr := recover(); rerr != nil {
|
||||
switch rerr := rerr.(type) {
|
||||
case Error:
|
||||
err = errors.Wrap(rerr, v.ShortSummary())
|
||||
default:
|
||||
panic(fmt.Sprintf("mapper %T failed to apply to %s: %s", v.Mapper, v.Summary(), rerr))
|
||||
}
|
||||
}
|
||||
}()
|
||||
err = v.Mapper.Decode(&DecodeContext{Value: v, Scan: scan}, target)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, v.ShortSummary())
|
||||
}
|
||||
v.Set = true
|
||||
return nil
|
||||
}
|
||||
|
||||
// Apply value to field.
|
||||
func (v *Value) Apply(value reflect.Value) {
|
||||
v.Target.Set(value)
|
||||
v.Set = true
|
||||
}
|
||||
|
||||
// ApplyDefault value to field if it is not already set.
|
||||
func (v *Value) ApplyDefault() error {
|
||||
if reflectValueIsZero(v.Target) {
|
||||
return v.Reset()
|
||||
}
|
||||
v.Set = true
|
||||
return nil
|
||||
}
|
||||
|
||||
// Reset this value to its default, either the zero value or the parsed result of its envar,
|
||||
// or its "default" tag.
|
||||
//
|
||||
// Does not include resolvers.
|
||||
func (v *Value) Reset() error {
|
||||
v.Target.Set(reflect.Zero(v.Target.Type()))
|
||||
if v.Tag.Env != "" {
|
||||
envar := os.Getenv(v.Tag.Env)
|
||||
if envar != "" {
|
||||
err := v.Parse(ScanFromTokens(Token{Type: FlagValueToken, Value: envar}), v.Target)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s (from envar %s=%q)", err, v.Tag.Env, envar)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
if v.Default != "" {
|
||||
return v.Parse(ScanFromTokens(Token{Type: FlagValueToken, Value: v.Default}), v.Target)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (*Value) node() {}
|
||||
|
||||
// A Positional represents a non-branching command-line positional argument.
|
||||
type Positional = Value
|
||||
|
||||
// A Flag represents a command-line flag.
|
||||
type Flag struct {
|
||||
*Value
|
||||
Group string // Logical grouping when displaying. May also be used by configuration loaders to group options logically.
|
||||
Xor string
|
||||
PlaceHolder string
|
||||
Env string
|
||||
Short rune
|
||||
Hidden bool
|
||||
}
|
||||
|
||||
func (f *Flag) String() string {
|
||||
out := "--" + f.Name
|
||||
if f.Short != 0 {
|
||||
out = fmt.Sprintf("-%c, %s", f.Short, out)
|
||||
}
|
||||
if !f.IsBool() {
|
||||
out += "=" + f.FormatPlaceHolder()
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// FormatPlaceHolder formats the placeholder string for a Flag.
|
||||
func (f *Flag) FormatPlaceHolder() string {
|
||||
tail := ""
|
||||
if f.Value.IsSlice() && f.Value.Tag.Sep != -1 {
|
||||
tail += string(f.Value.Tag.Sep) + "..."
|
||||
}
|
||||
if f.Default != "" {
|
||||
if f.Value.Target.Kind() == reflect.String {
|
||||
return strconv.Quote(f.Default) + tail
|
||||
}
|
||||
return f.Default + tail
|
||||
}
|
||||
if f.PlaceHolder != "" {
|
||||
return f.PlaceHolder + tail
|
||||
}
|
||||
if f.Value.IsMap() {
|
||||
if f.Value.Tag.MapSep != -1 {
|
||||
tail = string(f.Value.Tag.MapSep) + "..."
|
||||
}
|
||||
return "KEY=VALUE" + tail
|
||||
}
|
||||
return strings.ToUpper(f.Name) + tail
|
||||
}
|
||||
|
||||
// This is directly from the Go 1.13 source code.
|
||||
func reflectValueIsZero(v reflect.Value) bool {
|
||||
switch v.Kind() {
|
||||
case reflect.Bool:
|
||||
return !v.Bool()
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return v.Int() == 0
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
return v.Uint() == 0
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return math.Float64bits(v.Float()) == 0
|
||||
case reflect.Complex64, reflect.Complex128:
|
||||
c := v.Complex()
|
||||
return math.Float64bits(real(c)) == 0 && math.Float64bits(imag(c)) == 0
|
||||
case reflect.Array:
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
if !reflectValueIsZero(v.Index(i)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice, reflect.UnsafePointer:
|
||||
return v.IsNil()
|
||||
case reflect.String:
|
||||
return v.Len() == 0
|
||||
case reflect.Struct:
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
if !reflectValueIsZero(v.Field(i)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
default:
|
||||
// This should never happens, but will act as a safeguard for
|
||||
// later, as a default value doesn't makes sense here.
|
||||
panic(&reflect.ValueError{"reflect.Value.IsZero", v.Kind()})
|
||||
}
|
||||
}
|
||||
289
vendor/github.com/alecthomas/kong/options.go
generated
vendored
Normal file
289
vendor/github.com/alecthomas/kong/options.go
generated
vendored
Normal file
@@ -0,0 +1,289 @@
|
||||
package kong
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// An Option applies optional changes to the Kong application.
|
||||
type Option interface {
|
||||
Apply(k *Kong) error
|
||||
}
|
||||
|
||||
// OptionFunc is function that adheres to the Option interface.
|
||||
type OptionFunc func(k *Kong) error
|
||||
|
||||
func (o OptionFunc) Apply(k *Kong) error { return o(k) } // nolint: golint
|
||||
|
||||
// Vars sets the variables to use for interpolation into help strings and default values.
|
||||
//
|
||||
// See README for details.
|
||||
type Vars map[string]string
|
||||
|
||||
// Apply lets Vars act as an Option.
|
||||
func (v Vars) Apply(k *Kong) error {
|
||||
for key, value := range v {
|
||||
k.vars[key] = value
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CloneWith clones the current Vars and merges "vars" onto the clone.
|
||||
func (v Vars) CloneWith(vars Vars) Vars {
|
||||
out := make(Vars, len(v)+len(vars))
|
||||
for key, value := range v {
|
||||
out[key] = value
|
||||
}
|
||||
for key, value := range vars {
|
||||
out[key] = value
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// Exit overrides the function used to terminate. This is useful for testing or interactive use.
|
||||
func Exit(exit func(int)) Option {
|
||||
return OptionFunc(func(k *Kong) error {
|
||||
k.Exit = exit
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// NoDefaultHelp disables the default help flags.
|
||||
func NoDefaultHelp() Option {
|
||||
return OptionFunc(func(k *Kong) error {
|
||||
k.noDefaultHelp = true
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// PostBuild provides read/write access to kong.Kong after initial construction of the model is complete but before
|
||||
// parsing occurs.
|
||||
//
|
||||
// This is useful for, e.g., adding short options to flags, updating help, etc.
|
||||
func (k *Kong) PostBuild(fn func(*Kong) error) Option {
|
||||
return OptionFunc(func(k *Kong) error {
|
||||
k.postBuildOptions = append(k.postBuildOptions, OptionFunc(fn))
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// Name overrides the application name.
|
||||
func Name(name string) Option {
|
||||
return OptionFunc(func(k *Kong) error {
|
||||
k.postBuildOptions = append(k.postBuildOptions, OptionFunc(func(k *Kong) error {
|
||||
k.Model.Name = name
|
||||
return nil
|
||||
}))
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// Description sets the application description.
|
||||
func Description(description string) Option {
|
||||
return OptionFunc(func(k *Kong) error {
|
||||
k.postBuildOptions = append(k.postBuildOptions, OptionFunc(func(k *Kong) error {
|
||||
k.Model.Help = description
|
||||
return nil
|
||||
}))
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// TypeMapper registers a mapper to a type.
|
||||
func TypeMapper(typ reflect.Type, mapper Mapper) Option {
|
||||
return OptionFunc(func(k *Kong) error {
|
||||
k.registry.RegisterType(typ, mapper)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// KindMapper registers a mapper to a kind.
|
||||
func KindMapper(kind reflect.Kind, mapper Mapper) Option {
|
||||
return OptionFunc(func(k *Kong) error {
|
||||
k.registry.RegisterKind(kind, mapper)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// ValueMapper registers a mapper to a field value.
|
||||
func ValueMapper(ptr interface{}, mapper Mapper) Option {
|
||||
return OptionFunc(func(k *Kong) error {
|
||||
k.registry.RegisterValue(ptr, mapper)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// NamedMapper registers a mapper to a name.
|
||||
func NamedMapper(name string, mapper Mapper) Option {
|
||||
return OptionFunc(func(k *Kong) error {
|
||||
k.registry.RegisterName(name, mapper)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// Writers overrides the default writers. Useful for testing or interactive use.
|
||||
func Writers(stdout, stderr io.Writer) Option {
|
||||
return OptionFunc(func(k *Kong) error {
|
||||
k.Stdout = stdout
|
||||
k.Stderr = stderr
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// Bind binds values for hooks and Run() function arguments.
|
||||
//
|
||||
// Any arguments passed will be available to the receiving hook functions, but may be omitted. Additionally, *Kong and
|
||||
// the current *Context will also be made available.
|
||||
//
|
||||
// There are two hook points:
|
||||
//
|
||||
// BeforeApply(...) error
|
||||
// AfterApply(...) error
|
||||
//
|
||||
// Called before validation/assignment, and immediately after validation/assignment, respectively.
|
||||
func Bind(args ...interface{}) Option {
|
||||
return OptionFunc(func(k *Kong) error {
|
||||
k.bindings.add(args...)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// BindTo allows binding of implementations to interfaces.
|
||||
//
|
||||
// BindTo(impl, (*iface)(nil))
|
||||
func BindTo(impl, iface interface{}) Option {
|
||||
return OptionFunc(func(k *Kong) error {
|
||||
valueOf := reflect.ValueOf(impl)
|
||||
k.bindings[reflect.TypeOf(iface).Elem()] = func() (reflect.Value, error) { return valueOf, nil }
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// 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.
|
||||
func BindToProvider(provider interface{}) Option {
|
||||
return OptionFunc(func(k *Kong) error {
|
||||
pv := reflect.ValueOf(provider)
|
||||
t := pv.Type()
|
||||
if t.Kind() != reflect.Func || t.NumIn() != 0 || t.NumOut() != 2 || t.Out(1) != reflect.TypeOf((*error)(nil)).Elem() {
|
||||
return errors.Errorf("%T must be a function with the signature func()(T, error)", provider)
|
||||
}
|
||||
rt := pv.Type().Out(0)
|
||||
k.bindings[rt] = func() (reflect.Value, error) {
|
||||
out := pv.Call(nil)
|
||||
errv := out[1]
|
||||
var err error
|
||||
if !errv.IsNil() {
|
||||
err = errv.Interface().(error)
|
||||
}
|
||||
return out[0], err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// Help printer to use.
|
||||
func Help(help HelpPrinter) Option {
|
||||
return OptionFunc(func(k *Kong) error {
|
||||
k.help = help
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// HelpFormatter configures how the help text is formatted.
|
||||
func HelpFormatter(helpFormatter HelpValueFormatter) Option {
|
||||
return OptionFunc(func(k *Kong) error {
|
||||
k.helpFormatter = helpFormatter
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// ConfigureHelp sets the HelpOptions to use for printing help.
|
||||
func ConfigureHelp(options HelpOptions) Option {
|
||||
return OptionFunc(func(k *Kong) error {
|
||||
k.helpOptions = options
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// UsageOnError configures Kong to display context-sensitive usage if FatalIfErrorf is called with an error.
|
||||
func UsageOnError() Option {
|
||||
return OptionFunc(func(k *Kong) error {
|
||||
k.usageOnError = true
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// ClearResolvers clears all existing resolvers.
|
||||
func ClearResolvers() Option {
|
||||
return OptionFunc(func(k *Kong) error {
|
||||
k.resolvers = nil
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// Resolvers registers flag resolvers.
|
||||
func Resolvers(resolvers ...Resolver) Option {
|
||||
return OptionFunc(func(k *Kong) error {
|
||||
k.resolvers = append(k.resolvers, resolvers...)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// ConfigurationLoader is a function that builds a resolver from a file.
|
||||
type ConfigurationLoader func(r io.Reader) (Resolver, error)
|
||||
|
||||
// Configuration provides Kong with support for loading defaults from a set of configuration files.
|
||||
//
|
||||
// Paths will be opened in order, and "loader" will be used to provide a Resolver which is registered with Kong.
|
||||
//
|
||||
// Note: The JSON function is a ConfigurationLoader.
|
||||
//
|
||||
// ~ and variable expansion will occur on the provided paths.
|
||||
func Configuration(loader ConfigurationLoader, paths ...string) Option {
|
||||
return OptionFunc(func(k *Kong) error {
|
||||
k.loader = loader
|
||||
for _, path := range paths {
|
||||
if _, err := os.Stat(ExpandPath(path)); os.IsNotExist(err) {
|
||||
continue
|
||||
}
|
||||
resolver, err := k.LoadConfig(path)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, path)
|
||||
}
|
||||
if resolver != nil {
|
||||
k.resolvers = append(k.resolvers, resolver)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// ExpandPath is a helper function to expand a relative or home-relative path to an absolute path.
|
||||
//
|
||||
// eg. ~/.someconf -> /home/alec/.someconf
|
||||
func ExpandPath(path string) string {
|
||||
if filepath.IsAbs(path) {
|
||||
return path
|
||||
}
|
||||
if strings.HasPrefix(path, "~/") {
|
||||
user, err := user.Current()
|
||||
if err != nil {
|
||||
return path
|
||||
}
|
||||
return filepath.Join(user.HomeDir, path[2:])
|
||||
}
|
||||
abspath, err := filepath.Abs(path)
|
||||
if err != nil {
|
||||
return path
|
||||
}
|
||||
return abspath
|
||||
}
|
||||
49
vendor/github.com/alecthomas/kong/resolver.go
generated
vendored
Normal file
49
vendor/github.com/alecthomas/kong/resolver.go
generated
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
package kong
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// A Resolver resolves a Flag value from an external source.
|
||||
type Resolver interface {
|
||||
// Validate configuration against Application.
|
||||
//
|
||||
// This can be used to validate that all provided configuration is valid within this application.
|
||||
Validate(app *Application) error
|
||||
|
||||
// Resolve the value for a Flag.
|
||||
Resolve(context *Context, parent *Path, flag *Flag) (interface{}, error)
|
||||
}
|
||||
|
||||
// ResolverFunc is a convenience type for non-validating Resolvers.
|
||||
type ResolverFunc func(context *Context, parent *Path, flag *Flag) (interface{}, error)
|
||||
|
||||
var _ Resolver = ResolverFunc(nil)
|
||||
|
||||
func (r ResolverFunc) Resolve(context *Context, parent *Path, flag *Flag) (interface{}, error) { // nolint: golint
|
||||
return r(context, parent, flag)
|
||||
}
|
||||
func (r ResolverFunc) Validate(app *Application) error { return nil } // nolint: golint
|
||||
|
||||
// JSON returns a Resolver that retrieves values from a JSON source.
|
||||
//
|
||||
// Hyphens in flag names are replaced with underscores.
|
||||
func JSON(r io.Reader) (Resolver, error) {
|
||||
values := map[string]interface{}{}
|
||||
err := json.NewDecoder(r).Decode(&values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var f ResolverFunc = func(context *Context, parent *Path, flag *Flag) (interface{}, error) {
|
||||
name := strings.Replace(flag.Name, "-", "_", -1)
|
||||
raw, ok := values[name]
|
||||
if !ok {
|
||||
return nil, nil
|
||||
}
|
||||
return raw, nil
|
||||
}
|
||||
|
||||
return f, nil
|
||||
}
|
||||
217
vendor/github.com/alecthomas/kong/scanner.go
generated
vendored
Normal file
217
vendor/github.com/alecthomas/kong/scanner.go
generated
vendored
Normal file
@@ -0,0 +1,217 @@
|
||||
package kong
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// TokenType is the type of a token.
|
||||
type TokenType int
|
||||
|
||||
// Token types.
|
||||
const (
|
||||
UntypedToken TokenType = iota
|
||||
EOLToken
|
||||
FlagToken // --<flag>
|
||||
FlagValueToken // =<value>
|
||||
ShortFlagToken // -<short>[<tail]
|
||||
ShortFlagTailToken // <tail>
|
||||
PositionalArgumentToken // <arg>
|
||||
)
|
||||
|
||||
func (t TokenType) String() string {
|
||||
switch t {
|
||||
case UntypedToken:
|
||||
return "untyped"
|
||||
case EOLToken:
|
||||
return "<EOL>"
|
||||
case FlagToken: // --<flag>
|
||||
return "long flag"
|
||||
case FlagValueToken: // =<value>
|
||||
return "flag value"
|
||||
case ShortFlagToken: // -<short>[<tail]
|
||||
return "short flag"
|
||||
case ShortFlagTailToken: // <tail>
|
||||
return "short flag remainder"
|
||||
case PositionalArgumentToken: // <arg>
|
||||
return "positional argument"
|
||||
}
|
||||
panic("unsupported type")
|
||||
}
|
||||
|
||||
// Token created by Scanner.
|
||||
type Token struct {
|
||||
Value interface{}
|
||||
Type TokenType
|
||||
}
|
||||
|
||||
func (t Token) String() string {
|
||||
switch t.Type {
|
||||
case FlagToken:
|
||||
return fmt.Sprintf("--%v", t.Value)
|
||||
|
||||
case ShortFlagToken:
|
||||
return fmt.Sprintf("-%v", t.Value)
|
||||
|
||||
case EOLToken:
|
||||
return "EOL"
|
||||
|
||||
default:
|
||||
return fmt.Sprintf("%v", t.Value)
|
||||
}
|
||||
}
|
||||
|
||||
// IsEOL returns true if this Token is past the end of the line.
|
||||
func (t Token) IsEOL() bool {
|
||||
return t.Type == EOLToken
|
||||
}
|
||||
|
||||
// IsAny returns true if the token's type is any of those provided.
|
||||
func (t TokenType) IsAny(types ...TokenType) bool {
|
||||
for _, typ := range types {
|
||||
if t == typ {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// InferredType tries to infer the type of a token.
|
||||
func (t Token) InferredType() TokenType {
|
||||
if t.Type != UntypedToken {
|
||||
return t.Type
|
||||
}
|
||||
if v, ok := t.Value.(string); ok {
|
||||
if strings.HasPrefix(v, "--") { // nolint: gocritic
|
||||
return FlagToken
|
||||
} else if v == "-" {
|
||||
return PositionalArgumentToken
|
||||
} else if strings.HasPrefix(v, "-") {
|
||||
return ShortFlagToken
|
||||
}
|
||||
}
|
||||
return t.Type
|
||||
}
|
||||
|
||||
// IsValue returns true if token is usable as a parseable value.
|
||||
//
|
||||
// A parseable value is either a value typed token, or an untyped token NOT starting with a hyphen.
|
||||
func (t Token) IsValue() bool {
|
||||
tt := t.InferredType()
|
||||
return tt.IsAny(FlagValueToken, ShortFlagTailToken, PositionalArgumentToken) ||
|
||||
(tt == UntypedToken && !strings.HasPrefix(t.String(), "-"))
|
||||
}
|
||||
|
||||
// Scanner is a stack-based scanner over command-line tokens.
|
||||
//
|
||||
// Initially all tokens are untyped. As the parser consumes tokens it assigns types, splits tokens, and pushes them back
|
||||
// onto the stream.
|
||||
//
|
||||
// For example, the token "--foo=bar" will be split into the following by the parser:
|
||||
//
|
||||
// [{FlagToken, "foo"}, {FlagValueToken, "bar"}]
|
||||
type Scanner struct {
|
||||
args []Token
|
||||
}
|
||||
|
||||
// Scan creates a new Scanner from args with untyped tokens.
|
||||
func Scan(args ...string) *Scanner {
|
||||
s := &Scanner{}
|
||||
for _, arg := range args {
|
||||
s.args = append(s.args, Token{Value: arg})
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// ScanFromTokens creates a new Scanner from a slice of tokens.
|
||||
func ScanFromTokens(tokens ...Token) *Scanner {
|
||||
return &Scanner{args: tokens}
|
||||
}
|
||||
|
||||
// Len returns the number of input arguments.
|
||||
func (s *Scanner) Len() int {
|
||||
return len(s.args)
|
||||
}
|
||||
|
||||
// Pop the front token off the Scanner.
|
||||
func (s *Scanner) Pop() Token {
|
||||
if len(s.args) == 0 {
|
||||
return Token{Type: EOLToken}
|
||||
}
|
||||
arg := s.args[0]
|
||||
s.args = s.args[1:]
|
||||
return arg
|
||||
}
|
||||
|
||||
type expectedError struct {
|
||||
context string
|
||||
token Token
|
||||
}
|
||||
|
||||
func (e *expectedError) Error() string {
|
||||
return fmt.Sprintf("expected %s value but got %q (%s)", e.context, e.token, e.token.InferredType())
|
||||
}
|
||||
|
||||
// PopValue pops a value token, or returns an error.
|
||||
//
|
||||
// "context" is used to assist the user if the value can not be popped, eg. "expected <context> value but got <type>"
|
||||
func (s *Scanner) PopValue(context string) (Token, error) {
|
||||
t := s.Pop()
|
||||
if !t.IsValue() {
|
||||
return t, &expectedError{context, t}
|
||||
}
|
||||
return t, nil
|
||||
}
|
||||
|
||||
// PopValueInto pops a value token into target or returns an error.
|
||||
//
|
||||
// "context" is used to assist the user if the value can not be popped, eg. "expected <context> value but got <type>"
|
||||
func (s *Scanner) PopValueInto(context string, target interface{}) error {
|
||||
t, err := s.PopValue(context)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return jsonTranscode(t.Value, target)
|
||||
}
|
||||
|
||||
// PopWhile predicate returns true.
|
||||
func (s *Scanner) PopWhile(predicate func(Token) bool) (values []Token) {
|
||||
for predicate(s.Peek()) {
|
||||
values = append(values, s.Pop())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// PopUntil predicate returns true.
|
||||
func (s *Scanner) PopUntil(predicate func(Token) bool) (values []Token) {
|
||||
for !predicate(s.Peek()) {
|
||||
values = append(values, s.Pop())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Peek at the next Token or return an EOLToken.
|
||||
func (s *Scanner) Peek() Token {
|
||||
if len(s.args) == 0 {
|
||||
return Token{Type: EOLToken}
|
||||
}
|
||||
return s.args[0]
|
||||
}
|
||||
|
||||
// Push an untyped Token onto the front of the Scanner.
|
||||
func (s *Scanner) Push(arg interface{}) *Scanner {
|
||||
s.PushToken(Token{Value: arg})
|
||||
return s
|
||||
}
|
||||
|
||||
// PushTyped pushes a typed token onto the front of the Scanner.
|
||||
func (s *Scanner) PushTyped(arg interface{}, typ TokenType) *Scanner {
|
||||
s.PushToken(Token{Value: arg, Type: typ})
|
||||
return s
|
||||
}
|
||||
|
||||
// PushToken pushes a preconstructed Token onto the front of the Scanner.
|
||||
func (s *Scanner) PushToken(token Token) *Scanner {
|
||||
s.args = append([]Token{token}, s.args...)
|
||||
return s
|
||||
}
|
||||
240
vendor/github.com/alecthomas/kong/tag.go
generated
vendored
Normal file
240
vendor/github.com/alecthomas/kong/tag.go
generated
vendored
Normal file
@@ -0,0 +1,240 @@
|
||||
package kong
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// Tag represents the parsed state of Kong tags in a struct field tag.
|
||||
type Tag struct {
|
||||
Ignored bool // Field is ignored by Kong. ie. kong:"-"
|
||||
Cmd bool
|
||||
Arg bool
|
||||
Required bool
|
||||
Optional bool
|
||||
Name string
|
||||
Help string
|
||||
Type string
|
||||
Default string
|
||||
Format string
|
||||
PlaceHolder string
|
||||
Env string
|
||||
Short rune
|
||||
Hidden bool
|
||||
Sep rune
|
||||
MapSep rune
|
||||
Enum string
|
||||
Group string
|
||||
Xor string
|
||||
Vars Vars
|
||||
Prefix string // Optional prefix on anonymous structs. All sub-flags will have this prefix.
|
||||
Embed bool
|
||||
|
||||
// Storage for all tag keys for arbitrary lookups.
|
||||
items map[string][]string
|
||||
}
|
||||
|
||||
type tagChars struct {
|
||||
sep, quote, assign rune
|
||||
}
|
||||
|
||||
var kongChars = tagChars{sep: ',', quote: '\'', assign: '='}
|
||||
var bareChars = tagChars{sep: ' ', quote: '"', assign: ':'}
|
||||
|
||||
func parseTagItems(tagString string, chr tagChars) map[string][]string {
|
||||
d := map[string][]string{}
|
||||
key := []rune{}
|
||||
value := []rune{}
|
||||
quotes := false
|
||||
inKey := true
|
||||
|
||||
add := func() {
|
||||
d[string(key)] = append(d[string(key)], string(value))
|
||||
key = []rune{}
|
||||
value = []rune{}
|
||||
inKey = true
|
||||
}
|
||||
|
||||
runes := []rune(tagString)
|
||||
for idx := 0; idx < len(runes); idx++ {
|
||||
r := runes[idx]
|
||||
next := rune(0)
|
||||
eof := false
|
||||
if idx < len(runes)-1 {
|
||||
next = runes[idx+1]
|
||||
} else {
|
||||
eof = true
|
||||
}
|
||||
if !quotes && r == chr.sep {
|
||||
add()
|
||||
continue
|
||||
}
|
||||
if r == chr.assign && inKey {
|
||||
inKey = false
|
||||
continue
|
||||
}
|
||||
if r == '\\' {
|
||||
if next == chr.quote {
|
||||
idx++
|
||||
r = chr.quote
|
||||
}
|
||||
} else if r == chr.quote {
|
||||
if quotes {
|
||||
quotes = false
|
||||
if next == chr.sep || eof {
|
||||
continue
|
||||
}
|
||||
fail("%v has an unexpected char at pos %v", tagString, idx)
|
||||
} else {
|
||||
quotes = true
|
||||
continue
|
||||
}
|
||||
}
|
||||
if inKey {
|
||||
key = append(key, r)
|
||||
} else {
|
||||
value = append(value, r)
|
||||
}
|
||||
}
|
||||
if quotes {
|
||||
fail("%v is not quoted properly", tagString)
|
||||
}
|
||||
|
||||
add()
|
||||
|
||||
return d
|
||||
}
|
||||
|
||||
func getTagInfo(ft reflect.StructField) (string, tagChars) {
|
||||
s, ok := ft.Tag.Lookup("kong")
|
||||
if ok {
|
||||
return s, kongChars
|
||||
}
|
||||
|
||||
return string(ft.Tag), bareChars
|
||||
}
|
||||
|
||||
func newEmptyTag() *Tag {
|
||||
return &Tag{items: map[string][]string{}}
|
||||
}
|
||||
|
||||
func parseTag(fv reflect.Value, ft reflect.StructField) *Tag {
|
||||
if ft.Tag.Get("kong") == "-" {
|
||||
t := newEmptyTag()
|
||||
t.Ignored = true
|
||||
return t
|
||||
}
|
||||
t := &Tag{
|
||||
items: parseTagItems(getTagInfo(ft)),
|
||||
}
|
||||
t.Cmd = t.Has("cmd")
|
||||
t.Arg = t.Has("arg")
|
||||
required := t.Has("required")
|
||||
optional := t.Has("optional")
|
||||
if required && optional {
|
||||
fail("can't specify both required and optional")
|
||||
}
|
||||
t.Required = required
|
||||
t.Optional = optional
|
||||
t.Default = t.Get("default")
|
||||
// Arguments with defaults are always optional.
|
||||
if t.Arg && t.Default != "" {
|
||||
t.Optional = true
|
||||
}
|
||||
t.Name = t.Get("name")
|
||||
t.Help = t.Get("help")
|
||||
t.Type = t.Get("type")
|
||||
t.Env = t.Get("env")
|
||||
t.Short, _ = t.GetRune("short")
|
||||
t.Hidden = t.Has("hidden")
|
||||
t.Format = t.Get("format")
|
||||
t.Sep, _ = t.GetSep("sep", ',')
|
||||
t.MapSep, _ = t.GetSep("mapsep", ';')
|
||||
t.Group = t.Get("group")
|
||||
t.Xor = t.Get("xor")
|
||||
t.Prefix = t.Get("prefix")
|
||||
t.Embed = t.Has("embed")
|
||||
t.Vars = Vars{}
|
||||
for _, set := range t.GetAll("set") {
|
||||
parts := strings.SplitN(set, "=", 2)
|
||||
if len(parts) == 0 {
|
||||
fail("set should be in the form key=value but got %q", set)
|
||||
}
|
||||
t.Vars[parts[0]] = parts[1]
|
||||
}
|
||||
t.PlaceHolder = t.Get("placeholder")
|
||||
if t.PlaceHolder == "" {
|
||||
t.PlaceHolder = strings.ToUpper(dashedString(fv.Type().Name()))
|
||||
}
|
||||
t.Enum = t.Get("enum")
|
||||
return t
|
||||
}
|
||||
|
||||
// Has returns true if the tag contained the given key.
|
||||
func (t *Tag) Has(k string) bool {
|
||||
_, ok := t.items[k]
|
||||
return ok
|
||||
}
|
||||
|
||||
// Get returns the value of the given tag.
|
||||
//
|
||||
// Note that this will return the empty string if the tag is missing.
|
||||
func (t *Tag) Get(k string) string {
|
||||
values := t.items[k]
|
||||
if len(values) == 0 {
|
||||
return ""
|
||||
}
|
||||
return values[0]
|
||||
}
|
||||
|
||||
// GetAll returns all encountered values for a tag, in the case of multiple occurrences.
|
||||
func (t *Tag) GetAll(k string) []string {
|
||||
return t.items[k]
|
||||
}
|
||||
|
||||
// GetBool returns true if the given tag looks like a boolean truth string.
|
||||
func (t *Tag) GetBool(k string) (bool, error) {
|
||||
return strconv.ParseBool(t.Get(k))
|
||||
}
|
||||
|
||||
// GetFloat parses the given tag as a float64.
|
||||
func (t *Tag) GetFloat(k string) (float64, error) {
|
||||
return strconv.ParseFloat(t.Get(k), 64)
|
||||
}
|
||||
|
||||
// GetInt parses the given tag as an int64.
|
||||
func (t *Tag) GetInt(k string) (int64, error) {
|
||||
return strconv.ParseInt(t.Get(k), 10, 64)
|
||||
}
|
||||
|
||||
// GetRune parses the given tag as a rune.
|
||||
func (t *Tag) GetRune(k string) (rune, error) {
|
||||
r, _ := utf8.DecodeRuneInString(t.Get(k))
|
||||
if r == utf8.RuneError {
|
||||
return 0, fmt.Errorf("%v has a rune error", t.Get(k))
|
||||
}
|
||||
return r, nil
|
||||
}
|
||||
|
||||
// GetSep parses the given tag as a rune separator, allowing for a default or none.
|
||||
// The separator is returned, or -1 if "none" is specified. If the tag value is an
|
||||
// invalid utf8 sequence, the default rune is returned as well as an error. If the
|
||||
// tag value is more than one rune, the first rune is returned as well as an error.
|
||||
func (t *Tag) GetSep(k string, dflt rune) (rune, error) {
|
||||
tv := t.Get(k)
|
||||
if tv == "none" {
|
||||
return -1, nil
|
||||
} else if tv == "" {
|
||||
return dflt, nil
|
||||
}
|
||||
r, size := utf8.DecodeRuneInString(tv)
|
||||
if r == utf8.RuneError {
|
||||
return dflt, fmt.Errorf(`%v:"%v" has a rune error`, k, tv)
|
||||
} else if size != len(tv) {
|
||||
return r, fmt.Errorf(`%v:"%v" is more than a single rune`, k, tv)
|
||||
}
|
||||
return r, nil
|
||||
}
|
||||
35
vendor/github.com/alecthomas/kong/util.go
generated
vendored
Normal file
35
vendor/github.com/alecthomas/kong/util.go
generated
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
package kong
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// ConfigFlag uses the configured (via kong.Configuration(loader)) configuration loader to load configuration
|
||||
// from a file specified by a flag.
|
||||
//
|
||||
// Use this as a flag value to support loading of custom configuration via a flag.
|
||||
type ConfigFlag string
|
||||
|
||||
// BeforeResolve adds a resolver.
|
||||
func (c ConfigFlag) BeforeResolve(kong *Kong, ctx *Context, trace *Path) error {
|
||||
if kong.loader == nil {
|
||||
return fmt.Errorf("kong must be configured with kong.Configuration(...)")
|
||||
}
|
||||
path := string(ctx.FlagValue(trace.Flag).(ConfigFlag))
|
||||
resolver, err := kong.LoadConfig(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ctx.AddResolver(resolver)
|
||||
return nil
|
||||
}
|
||||
|
||||
// VersionFlag is a flag type that can be used to display a version number, stored in the "version" variable.
|
||||
type VersionFlag bool
|
||||
|
||||
// BeforeApply writes the version variable and terminates with a 0 exit status.
|
||||
func (v VersionFlag) BeforeApply(app *Kong, vars Vars) error {
|
||||
fmt.Fprintln(app.Stdout, vars["version"])
|
||||
app.Exit(0)
|
||||
return nil
|
||||
}
|
||||
58
vendor/github.com/alecthomas/kong/visit.go
generated
vendored
Normal file
58
vendor/github.com/alecthomas/kong/visit.go
generated
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
package kong
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Next should be called by Visitor to proceed with the walk.
|
||||
//
|
||||
// The walk will terminate if "err" is non-nil.
|
||||
type Next func(err error) error
|
||||
|
||||
// Visitor can be used to walk all nodes in the model.
|
||||
type Visitor func(node Visitable, next Next) error
|
||||
|
||||
// Visit all nodes.
|
||||
func Visit(node Visitable, visitor Visitor) error {
|
||||
return visitor(node, func(err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch node := node.(type) {
|
||||
case *Application:
|
||||
return visitNodeChildren(node.Node, visitor)
|
||||
case *Node:
|
||||
return visitNodeChildren(node, visitor)
|
||||
case *Value:
|
||||
case *Flag:
|
||||
return Visit(node.Value, visitor)
|
||||
default:
|
||||
panic(fmt.Sprintf("unsupported node type %T", node))
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func visitNodeChildren(node *Node, visitor Visitor) error {
|
||||
if node.Argument != nil {
|
||||
if err := Visit(node.Argument, visitor); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for _, flag := range node.Flags {
|
||||
if err := Visit(flag, visitor); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for _, pos := range node.Positional {
|
||||
if err := Visit(pos, visitor); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for _, child := range node.Children {
|
||||
if err := Visit(child, visitor); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user