⬆️ update dependencies, implement user store

This commit is contained in:
2020-10-09 18:35:44 +02:00
parent d2c0ebb043
commit 129df9d7f2
90 changed files with 6640 additions and 12741 deletions

1
vendor/github.com/alecthomas/kong/.gitignore generated vendored Normal file
View File

@@ -0,0 +1 @@
bin

42
vendor/github.com/alecthomas/kong/.golangci.yml generated vendored Normal file
View 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
View 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
View 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
[![](https://godoc.org/github.com/alecthomas/kong?status.svg)](http://godoc.org/github.com/alecthomas/kong) [![CircleCI](https://img.shields.io/circleci/project/github/alecthomas/kong.svg)](https://circleci.com/gh/alecthomas/kong) [![Go Report Card](https://goreportcard.com/badge/github.com/alecthomas/kong)](https://goreportcard.com/report/github.com/alecthomas/kong) [![Slack chat](https://img.shields.io/static/v1?logo=slack&style=flat&label=slack&color=green&message=gophers)](https://gophers.slack.com/messages/CN9DS8YF3)
[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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
}