mirror of
https://github.com/Threnklyn/jira.git
synced 2026-06-14 00:23:32 +02:00
udpate to use latest dep, update figtree
This commit is contained in:
-4
@@ -1,4 +0,0 @@
|
||||
sudo: false
|
||||
language: go
|
||||
install: go get -t -v ./...
|
||||
go: 1.2
|
||||
-674
@@ -1,674 +0,0 @@
|
||||
# Kingpin - A Go (golang) command line and flag parser
|
||||
[](http://godoc.org/github.com/alecthomas/kingpin) [](https://travis-ci.org/alecthomas/kingpin) [](https://gitter.im/alecthomas/Lobby)
|
||||
|
||||
|
||||
|
||||
<!-- MarkdownTOC -->
|
||||
|
||||
- [Overview](#overview)
|
||||
- [Features](#features)
|
||||
- [User-visible changes between v1 and v2](#user-visible-changes-between-v1-and-v2)
|
||||
- [Flags can be used at any point after their definition.](#flags-can-be-used-at-any-point-after-their-definition)
|
||||
- [Short flags can be combined with their parameters](#short-flags-can-be-combined-with-their-parameters)
|
||||
- [API changes between v1 and v2](#api-changes-between-v1-and-v2)
|
||||
- [Versions](#versions)
|
||||
- [V2 is the current stable version](#v2-is-the-current-stable-version)
|
||||
- [V1 is the OLD stable version](#v1-is-the-old-stable-version)
|
||||
- [Change History](#change-history)
|
||||
- [Examples](#examples)
|
||||
- [Simple Example](#simple-example)
|
||||
- [Complex Example](#complex-example)
|
||||
- [Reference Documentation](#reference-documentation)
|
||||
- [Displaying errors and usage information](#displaying-errors-and-usage-information)
|
||||
- [Sub-commands](#sub-commands)
|
||||
- [Custom Parsers](#custom-parsers)
|
||||
- [Repeatable flags](#repeatable-flags)
|
||||
- [Boolean Values](#boolean-values)
|
||||
- [Default Values](#default-values)
|
||||
- [Place-holders in Help](#place-holders-in-help)
|
||||
- [Consuming all remaining arguments](#consuming-all-remaining-arguments)
|
||||
- [Bash/ZSH Shell Completion](#bashzsh-shell-completion)
|
||||
- [Supporting -h for help](#supporting--h-for-help)
|
||||
- [Custom help](#custom-help)
|
||||
|
||||
<!-- /MarkdownTOC -->
|
||||
|
||||
## Overview
|
||||
|
||||
Kingpin is a [fluent-style](http://en.wikipedia.org/wiki/Fluent_interface),
|
||||
type-safe command-line parser. It supports flags, nested commands, and
|
||||
positional arguments.
|
||||
|
||||
Install it with:
|
||||
|
||||
$ go get gopkg.in/alecthomas/kingpin.v2
|
||||
|
||||
It looks like this:
|
||||
|
||||
```go
|
||||
var (
|
||||
verbose = kingpin.Flag("verbose", "Verbose mode.").Short('v').Bool()
|
||||
name = kingpin.Arg("name", "Name of user.").Required().String()
|
||||
)
|
||||
|
||||
func main() {
|
||||
kingpin.Parse()
|
||||
fmt.Printf("%v, %s\n", *verbose, *name)
|
||||
}
|
||||
```
|
||||
|
||||
More [examples](https://github.com/alecthomas/kingpin/tree/master/_examples) are available.
|
||||
|
||||
Second to parsing, providing the user with useful help is probably the most
|
||||
important thing a command-line parser does. Kingpin tries to provide detailed
|
||||
contextual help if `--help` is encountered at any point in the command line
|
||||
(excluding after `--`).
|
||||
|
||||
## Features
|
||||
|
||||
- Help output that isn't as ugly as sin.
|
||||
- Fully [customisable help](#custom-help), via Go templates.
|
||||
- Parsed, type-safe flags (`kingpin.Flag("f", "help").Int()`)
|
||||
- Parsed, type-safe positional arguments (`kingpin.Arg("a", "help").Int()`).
|
||||
- Parsed, type-safe, arbitrarily deep commands (`kingpin.Command("c", "help")`).
|
||||
- Support for required flags and required positional arguments (`kingpin.Flag("f", "").Required().Int()`).
|
||||
- Support for arbitrarily nested default commands (`command.Default()`).
|
||||
- Callbacks per command, flag and argument (`kingpin.Command("c", "").Action(myAction)`).
|
||||
- POSIX-style short flag combining (`-a -b` -> `-ab`).
|
||||
- Short-flag+parameter combining (`-a parm` -> `-aparm`).
|
||||
- Read command-line from files (`@<file>`).
|
||||
- Automatically generate man pages (`--help-man`).
|
||||
|
||||
## User-visible changes between v1 and v2
|
||||
|
||||
### Flags can be used at any point after their definition.
|
||||
|
||||
Flags can be specified at any point after their definition, not just
|
||||
*immediately after their associated command*. From the chat example below, the
|
||||
following used to be required:
|
||||
|
||||
```
|
||||
$ chat --server=chat.server.com:8080 post --image=~/Downloads/owls.jpg pics
|
||||
```
|
||||
|
||||
But the following will now work:
|
||||
|
||||
```
|
||||
$ chat post --server=chat.server.com:8080 --image=~/Downloads/owls.jpg pics
|
||||
```
|
||||
|
||||
### Short flags can be combined with their parameters
|
||||
|
||||
Previously, if a short flag was used, any argument to that flag would have to
|
||||
be separated by a space. That is no longer the case.
|
||||
|
||||
## API changes between v1 and v2
|
||||
|
||||
- `ParseWithFileExpansion()` is gone. The new parser directly supports expanding `@<file>`.
|
||||
- Added `FatalUsage()` and `FatalUsageContext()` for displaying an error + usage and terminating.
|
||||
- `Dispatch()` renamed to `Action()`.
|
||||
- Added `ParseContext()` for parsing a command line into its intermediate context form without executing.
|
||||
- Added `Terminate()` function to override the termination function.
|
||||
- Added `UsageForContextWithTemplate()` for printing usage via a custom template.
|
||||
- Added `UsageTemplate()` for overriding the default template to use. Two templates are included:
|
||||
1. `DefaultUsageTemplate` - default template.
|
||||
2. `CompactUsageTemplate` - compact command template for larger applications.
|
||||
|
||||
## Versions
|
||||
|
||||
Kingpin uses [gopkg.in](https://gopkg.in/alecthomas/kingpin) for versioning.
|
||||
|
||||
The current stable version is [gopkg.in/alecthomas/kingpin.v2](https://gopkg.in/alecthomas/kingpin.v2). The previous version, [gopkg.in/alecthomas/kingpin.v1](https://gopkg.in/alecthomas/kingpin.v1), is deprecated and in maintenance mode.
|
||||
|
||||
### [V2](https://gopkg.in/alecthomas/kingpin.v2) is the current stable version
|
||||
|
||||
Installation:
|
||||
|
||||
```sh
|
||||
$ go get gopkg.in/alecthomas/kingpin.v2
|
||||
```
|
||||
|
||||
### [V1](https://gopkg.in/alecthomas/kingpin.v1) is the OLD stable version
|
||||
|
||||
Installation:
|
||||
|
||||
```sh
|
||||
$ go get gopkg.in/alecthomas/kingpin.v1
|
||||
```
|
||||
|
||||
## Change History
|
||||
|
||||
- *2015-09-19* -- Stable v2.1.0 release.
|
||||
- Added `command.Default()` to specify a default command to use if no other
|
||||
command matches. This allows for convenient user shortcuts.
|
||||
- Exposed `HelpFlag` and `VersionFlag` for further customisation.
|
||||
- `Action()` and `PreAction()` added and both now support an arbitrary
|
||||
number of callbacks.
|
||||
- `kingpin.SeparateOptionalFlagsUsageTemplate`.
|
||||
- `--help-long` and `--help-man` (hidden by default) flags.
|
||||
- Flags are "interspersed" by default, but can be disabled with `app.Interspersed(false)`.
|
||||
- Added flags for all simple builtin types (int8, uint16, etc.) and slice variants.
|
||||
- Use `app.Writer(os.Writer)` to specify the default writer for all output functions.
|
||||
- Dropped `os.Writer` prefix from all printf-like functions.
|
||||
|
||||
- *2015-05-22* -- Stable v2.0.0 release.
|
||||
- Initial stable release of v2.0.0.
|
||||
- Fully supports interspersed flags, commands and arguments.
|
||||
- Flags can be present at any point after their logical definition.
|
||||
- Application.Parse() terminates if commands are present and a command is not parsed.
|
||||
- Dispatch() -> Action().
|
||||
- Actions are dispatched after all values are populated.
|
||||
- Override termination function (defaults to os.Exit).
|
||||
- Override output stream (defaults to os.Stderr).
|
||||
- Templatised usage help, with default and compact templates.
|
||||
- Make error/usage functions more consistent.
|
||||
- Support argument expansion from files by default (with @<file>).
|
||||
- Fully public data model is available via .Model().
|
||||
- Parser has been completely refactored.
|
||||
- Parsing and execution has been split into distinct stages.
|
||||
- Use `go generate` to generate repeated flags.
|
||||
- Support combined short-flag+argument: -fARG.
|
||||
|
||||
- *2015-01-23* -- Stable v1.3.4 release.
|
||||
- Support "--" for separating flags from positional arguments.
|
||||
- Support loading flags from files (ParseWithFileExpansion()). Use @FILE as an argument.
|
||||
- Add post-app and post-cmd validation hooks. This allows arbitrary validation to be added.
|
||||
- A bunch of improvements to help usage and formatting.
|
||||
- Support arbitrarily nested sub-commands.
|
||||
|
||||
- *2014-07-08* -- Stable v1.2.0 release.
|
||||
- Pass any value through to `Strings()` when final argument.
|
||||
Allows for values that look like flags to be processed.
|
||||
- Allow `--help` to be used with commands.
|
||||
- Support `Hidden()` flags.
|
||||
- Parser for [units.Base2Bytes](https://github.com/alecthomas/units)
|
||||
type. Allows for flags like `--ram=512MB` or `--ram=1GB`.
|
||||
- Add an `Enum()` value, allowing only one of a set of values
|
||||
to be selected. eg. `Flag(...).Enum("debug", "info", "warning")`.
|
||||
|
||||
- *2014-06-27* -- Stable v1.1.0 release.
|
||||
- Bug fixes.
|
||||
- Always return an error (rather than panicing) when misconfigured.
|
||||
- `OpenFile(flag, perm)` value type added, for finer control over opening files.
|
||||
- Significantly improved usage formatting.
|
||||
|
||||
- *2014-06-19* -- Stable v1.0.0 release.
|
||||
- Support [cumulative positional](#consuming-all-remaining-arguments) arguments.
|
||||
- Return error rather than panic when there are fatal errors not caught by
|
||||
the type system. eg. when a default value is invalid.
|
||||
- Use gokpg.in.
|
||||
|
||||
- *2014-06-10* -- Place-holder streamlining.
|
||||
- Renamed `MetaVar` to `PlaceHolder`.
|
||||
- Removed `MetaVarFromDefault`. Kingpin now uses [heuristics](#place-holders-in-help)
|
||||
to determine what to display.
|
||||
|
||||
## Examples
|
||||
|
||||
### Simple Example
|
||||
|
||||
Kingpin can be used for simple flag+arg applications like so:
|
||||
|
||||
```
|
||||
$ ping --help
|
||||
usage: ping [<flags>] <ip> [<count>]
|
||||
|
||||
Flags:
|
||||
--debug Enable debug mode.
|
||||
--help Show help.
|
||||
-t, --timeout=5s Timeout waiting for ping.
|
||||
|
||||
Args:
|
||||
<ip> IP address to ping.
|
||||
[<count>] Number of packets to send
|
||||
$ ping 1.2.3.4 5
|
||||
Would ping: 1.2.3.4 with timeout 5s and count 0
|
||||
```
|
||||
|
||||
From the following source:
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"gopkg.in/alecthomas/kingpin.v2"
|
||||
)
|
||||
|
||||
var (
|
||||
debug = kingpin.Flag("debug", "Enable debug mode.").Bool()
|
||||
timeout = kingpin.Flag("timeout", "Timeout waiting for ping.").Default("5s").OverrideDefaultFromEnvar("PING_TIMEOUT").Short('t').Duration()
|
||||
ip = kingpin.Arg("ip", "IP address to ping.").Required().IP()
|
||||
count = kingpin.Arg("count", "Number of packets to send").Int()
|
||||
)
|
||||
|
||||
func main() {
|
||||
kingpin.Version("0.0.1")
|
||||
kingpin.Parse()
|
||||
fmt.Printf("Would ping: %s with timeout %s and count %d\n", *ip, *timeout, *count)
|
||||
}
|
||||
```
|
||||
|
||||
### Complex Example
|
||||
|
||||
Kingpin can also produce complex command-line applications with global flags,
|
||||
subcommands, and per-subcommand flags, like this:
|
||||
|
||||
```
|
||||
$ chat --help
|
||||
usage: chat [<flags>] <command> [<flags>] [<args> ...]
|
||||
|
||||
A command-line chat application.
|
||||
|
||||
Flags:
|
||||
--help Show help.
|
||||
--debug Enable debug mode.
|
||||
--server=127.0.0.1 Server address.
|
||||
|
||||
Commands:
|
||||
help [<command>]
|
||||
Show help for a command.
|
||||
|
||||
register <nick> <name>
|
||||
Register a new user.
|
||||
|
||||
post [<flags>] <channel> [<text>]
|
||||
Post a message to a channel.
|
||||
|
||||
$ chat help post
|
||||
usage: chat [<flags>] post [<flags>] <channel> [<text>]
|
||||
|
||||
Post a message to a channel.
|
||||
|
||||
Flags:
|
||||
--image=IMAGE Image to post.
|
||||
|
||||
Args:
|
||||
<channel> Channel to post to.
|
||||
[<text>] Text to post.
|
||||
|
||||
$ chat post --image=~/Downloads/owls.jpg pics
|
||||
...
|
||||
```
|
||||
|
||||
From this code:
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
"gopkg.in/alecthomas/kingpin.v2"
|
||||
)
|
||||
|
||||
var (
|
||||
app = kingpin.New("chat", "A command-line chat application.")
|
||||
debug = app.Flag("debug", "Enable debug mode.").Bool()
|
||||
serverIP = app.Flag("server", "Server address.").Default("127.0.0.1").IP()
|
||||
|
||||
register = app.Command("register", "Register a new user.")
|
||||
registerNick = register.Arg("nick", "Nickname for user.").Required().String()
|
||||
registerName = register.Arg("name", "Name of user.").Required().String()
|
||||
|
||||
post = app.Command("post", "Post a message to a channel.")
|
||||
postImage = post.Flag("image", "Image to post.").File()
|
||||
postChannel = post.Arg("channel", "Channel to post to.").Required().String()
|
||||
postText = post.Arg("text", "Text to post.").Strings()
|
||||
)
|
||||
|
||||
func main() {
|
||||
switch kingpin.MustParse(app.Parse(os.Args[1:])) {
|
||||
// Register user
|
||||
case register.FullCommand():
|
||||
println(*registerNick)
|
||||
|
||||
// Post message
|
||||
case post.FullCommand():
|
||||
if *postImage != nil {
|
||||
}
|
||||
text := strings.Join(*postText, " ")
|
||||
println("Post:", text)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Reference Documentation
|
||||
|
||||
### Displaying errors and usage information
|
||||
|
||||
Kingpin exports a set of functions to provide consistent errors and usage
|
||||
information to the user.
|
||||
|
||||
Error messages look something like this:
|
||||
|
||||
<app>: error: <message>
|
||||
|
||||
The functions on `Application` are:
|
||||
|
||||
Function | Purpose
|
||||
---------|--------------
|
||||
`Errorf(format, args)` | Display a printf formatted error to the user.
|
||||
`Fatalf(format, args)` | As with Errorf, but also call the termination handler.
|
||||
`FatalUsage(format, args)` | As with Fatalf, but also print contextual usage information.
|
||||
`FatalUsageContext(context, format, args)` | As with Fatalf, but also print contextual usage information from a `ParseContext`.
|
||||
`FatalIfError(err, format, args)` | Conditionally print an error prefixed with format+args, then call the termination handler
|
||||
|
||||
There are equivalent global functions in the kingpin namespace for the default
|
||||
`kingpin.CommandLine` instance.
|
||||
|
||||
### Sub-commands
|
||||
|
||||
Kingpin supports nested sub-commands, with separate flag and positional
|
||||
arguments per sub-command. Note that positional arguments may only occur after
|
||||
sub-commands.
|
||||
|
||||
For example:
|
||||
|
||||
```go
|
||||
var (
|
||||
deleteCommand = kingpin.Command("delete", "Delete an object.")
|
||||
deleteUserCommand = deleteCommand.Command("user", "Delete a user.")
|
||||
deleteUserUIDFlag = deleteUserCommand.Flag("uid", "Delete user by UID rather than username.")
|
||||
deleteUserUsername = deleteUserCommand.Arg("username", "Username to delete.")
|
||||
deletePostCommand = deleteCommand.Command("post", "Delete a post.")
|
||||
)
|
||||
|
||||
func main() {
|
||||
switch kingpin.Parse() {
|
||||
case "delete user":
|
||||
case "delete post":
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Custom Parsers
|
||||
|
||||
Kingpin supports both flag and positional argument parsers for converting to
|
||||
Go types. For example, some included parsers are `Int()`, `Float()`,
|
||||
`Duration()` and `ExistingFile()` (see [parsers.go](./parsers.go) for a complete list of included parsers).
|
||||
|
||||
Parsers conform to Go's [`flag.Value`](http://godoc.org/flag#Value)
|
||||
interface, so any existing implementations will work.
|
||||
|
||||
For example, a parser for accumulating HTTP header values might look like this:
|
||||
|
||||
```go
|
||||
type HTTPHeaderValue http.Header
|
||||
|
||||
func (h *HTTPHeaderValue) Set(value string) error {
|
||||
parts := strings.SplitN(value, ":", 2)
|
||||
if len(parts) != 2 {
|
||||
return fmt.Errorf("expected HEADER:VALUE got '%s'", value)
|
||||
}
|
||||
(*http.Header)(h).Add(parts[0], parts[1])
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *HTTPHeaderValue) String() string {
|
||||
return ""
|
||||
}
|
||||
```
|
||||
|
||||
As a convenience, I would recommend something like this:
|
||||
|
||||
```go
|
||||
func HTTPHeader(s Settings) (target *http.Header) {
|
||||
target = &http.Header{}
|
||||
s.SetValue((*HTTPHeaderValue)(target))
|
||||
return
|
||||
}
|
||||
```
|
||||
|
||||
You would use it like so:
|
||||
|
||||
```go
|
||||
headers = HTTPHeader(kingpin.Flag("header", "Add a HTTP header to the request.").Short('H'))
|
||||
```
|
||||
|
||||
### Repeatable flags
|
||||
|
||||
Depending on the `Value` they hold, some flags may be repeated. The
|
||||
`IsCumulative() bool` function on `Value` tells if it's safe to call `Set()`
|
||||
multiple times or if an error should be raised if several values are passed.
|
||||
|
||||
The built-in `Value`s returning slices and maps, as well as `Counter` are
|
||||
examples of `Value`s that make a flag repeatable.
|
||||
|
||||
### Boolean values
|
||||
|
||||
Boolean values are uniquely managed by Kingpin. Each boolean flag will have a negative complement:
|
||||
`--<name>` and `--no-<name>`.
|
||||
|
||||
### Default Values
|
||||
|
||||
The default value is the zero value for a type. This can be overridden with
|
||||
the `Default(value...)` function on flags and arguments. This function accepts
|
||||
one or several strings, which are parsed by the value itself, so they *must*
|
||||
be compliant with the format expected.
|
||||
|
||||
### Place-holders in Help
|
||||
|
||||
The place-holder value for a flag is the value used in the help to describe
|
||||
the value of a non-boolean flag.
|
||||
|
||||
The value provided to PlaceHolder() is used if provided, then the value
|
||||
provided by Default() if provided, then finally the capitalised flag name is
|
||||
used.
|
||||
|
||||
Here are some examples of flags with various permutations:
|
||||
|
||||
--name=NAME // Flag(...).String()
|
||||
--name="Harry" // Flag(...).Default("Harry").String()
|
||||
--name=FULL-NAME // flag(...).PlaceHolder("FULL-NAME").Default("Harry").String()
|
||||
|
||||
### Consuming all remaining arguments
|
||||
|
||||
A common command-line idiom is to use all remaining arguments for some
|
||||
purpose. eg. The following command accepts an arbitrary number of
|
||||
IP addresses as positional arguments:
|
||||
|
||||
./cmd ping 10.1.1.1 192.168.1.1
|
||||
|
||||
Such arguments are similar to [repeatable flags](#repeatable-flags), but for
|
||||
arguments. Therefore they use the same `IsCumulative() bool` function on the
|
||||
underlying `Value`, so the built-in `Value`s for which the `Set()` function
|
||||
can be called several times will consume multiple arguments.
|
||||
|
||||
To implement the above example with a custom `Value`, we might do something
|
||||
like this:
|
||||
|
||||
```go
|
||||
type ipList []net.IP
|
||||
|
||||
func (i *ipList) Set(value string) error {
|
||||
if ip := net.ParseIP(value); ip == nil {
|
||||
return fmt.Errorf("'%s' is not an IP address", value)
|
||||
} else {
|
||||
*i = append(*i, ip)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (i *ipList) String() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (i *ipList) IsCumulative() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func IPList(s Settings) (target *[]net.IP) {
|
||||
target = new([]net.IP)
|
||||
s.SetValue((*ipList)(target))
|
||||
return
|
||||
}
|
||||
```
|
||||
|
||||
And use it like so:
|
||||
|
||||
```go
|
||||
ips := IPList(kingpin.Arg("ips", "IP addresses to ping."))
|
||||
```
|
||||
|
||||
### Bash/ZSH Shell Completion
|
||||
|
||||
By default, all flags and commands/subcommands generate completions
|
||||
internally.
|
||||
|
||||
Out of the box, CLI tools using kingpin should be able to take advantage
|
||||
of completion hinting for flags and commands. By specifying
|
||||
`--completion-bash` as the first argument, your CLI tool will show
|
||||
possible subcommands. By ending your argv with `--`, hints for flags
|
||||
will be shown.
|
||||
|
||||
To allow your end users to take advantage you must package a
|
||||
`/etc/bash_completion.d` script with your distribution (or the equivalent
|
||||
for your target platform/shell). An alternative is to instruct your end
|
||||
user to source a script from their `bash_profile` (or equivalent).
|
||||
|
||||
Fortunately Kingpin makes it easy to generate or source a script for use
|
||||
with end users shells. `./yourtool --completion-script-bash` and
|
||||
`./yourtool --completion-script-zsh` will generate these scripts for you.
|
||||
|
||||
**Installation by Package**
|
||||
|
||||
For the best user experience, you should bundle your pre-created
|
||||
completion script with your CLI tool and install it inside
|
||||
`/etc/bash_completion.d` (or equivalent). A good suggestion is to add
|
||||
this as an automated step to your build pipeline, in the implementation
|
||||
is improved for bug fixed.
|
||||
|
||||
**Installation by `bash_profile`**
|
||||
|
||||
Alternatively, instruct your users to add an additional statement to
|
||||
their `bash_profile` (or equivalent):
|
||||
|
||||
```
|
||||
eval "$(your-cli-tool --completion-script-bash)"
|
||||
```
|
||||
|
||||
Or for ZSH
|
||||
|
||||
```
|
||||
eval "$(your-cli-tool --completion-script-zsh)"
|
||||
```
|
||||
|
||||
#### Additional API
|
||||
To provide more flexibility, a completion option API has been
|
||||
exposed for flags to allow user defined completion options, to extend
|
||||
completions further than just EnumVar/Enum.
|
||||
|
||||
|
||||
**Provide Static Options**
|
||||
|
||||
When using an `Enum` or `EnumVar`, users are limited to only the options
|
||||
given. Maybe we wish to hint possible options to the user, but also
|
||||
allow them to provide their own custom option. `HintOptions` gives
|
||||
this functionality to flags.
|
||||
|
||||
```
|
||||
app := kingpin.New("completion", "My application with bash completion.")
|
||||
app.Flag("port", "Provide a port to connect to").
|
||||
Required().
|
||||
HintOptions("80", "443", "8080").
|
||||
IntVar(&c.port)
|
||||
```
|
||||
|
||||
**Provide Dynamic Options**
|
||||
Consider the case that you needed to read a local database or a file to
|
||||
provide suggestions. You can dynamically generate the options
|
||||
|
||||
```
|
||||
func listHosts() []string {
|
||||
// Provide a dynamic list of hosts from a hosts file or otherwise
|
||||
// for bash completion. In this example we simply return static slice.
|
||||
|
||||
// You could use this functionality to reach into a hosts file to provide
|
||||
// completion for a list of known hosts.
|
||||
return []string{"sshhost.example", "webhost.example", "ftphost.example"}
|
||||
}
|
||||
|
||||
app := kingpin.New("completion", "My application with bash completion.")
|
||||
app.Flag("flag-1", "").HintAction(listHosts).String()
|
||||
```
|
||||
|
||||
**EnumVar/Enum**
|
||||
When using `Enum` or `EnumVar`, any provided options will be automatically
|
||||
used for bash autocompletion. However, if you wish to provide a subset or
|
||||
different options, you can use `HintOptions` or `HintAction` which will override
|
||||
the default completion options for `Enum`/`EnumVar`.
|
||||
|
||||
|
||||
**Examples**
|
||||
You can see an in depth example of the completion API within
|
||||
`examples/completion/main.go`
|
||||
|
||||
|
||||
### Supporting -h for help
|
||||
|
||||
`kingpin.CommandLine.HelpFlag.Short('h')`
|
||||
|
||||
### Custom help
|
||||
|
||||
Kingpin v2 supports templatised help using the text/template library (actually, [a fork](https://github.com/alecthomas/template)).
|
||||
|
||||
You can specify the template to use with the [Application.UsageTemplate()](http://godoc.org/gopkg.in/alecthomas/kingpin.v2#Application.UsageTemplate) function.
|
||||
|
||||
There are four included templates: `kingpin.DefaultUsageTemplate` is the default,
|
||||
`kingpin.CompactUsageTemplate` provides a more compact representation for more complex command-line structures,
|
||||
`kingpin.SeparateOptionalFlagsUsageTemplate` looks like the default template, but splits required
|
||||
and optional command flags into separate lists, and `kingpin.ManPageTemplate` is used to generate man pages.
|
||||
|
||||
See the above templates for examples of usage, and the the function [UsageForContextWithTemplate()](https://github.com/alecthomas/kingpin/blob/master/usage.go#L198) method for details on the context.
|
||||
|
||||
#### Default help template
|
||||
|
||||
```
|
||||
$ go run ./examples/curl/curl.go --help
|
||||
usage: curl [<flags>] <command> [<args> ...]
|
||||
|
||||
An example implementation of curl.
|
||||
|
||||
Flags:
|
||||
--help Show help.
|
||||
-t, --timeout=5s Set connection timeout.
|
||||
-H, --headers=HEADER=VALUE
|
||||
Add HTTP headers to the request.
|
||||
|
||||
Commands:
|
||||
help [<command>...]
|
||||
Show help.
|
||||
|
||||
get url <url>
|
||||
Retrieve a URL.
|
||||
|
||||
get file <file>
|
||||
Retrieve a file.
|
||||
|
||||
post [<flags>] <url>
|
||||
POST a resource.
|
||||
```
|
||||
|
||||
#### Compact help template
|
||||
|
||||
```
|
||||
$ go run ./examples/curl/curl.go --help
|
||||
usage: curl [<flags>] <command> [<args> ...]
|
||||
|
||||
An example implementation of curl.
|
||||
|
||||
Flags:
|
||||
--help Show help.
|
||||
-t, --timeout=5s Set connection timeout.
|
||||
-H, --headers=HEADER=VALUE
|
||||
Add HTTP headers to the request.
|
||||
|
||||
Commands:
|
||||
help [<command>...]
|
||||
get [<flags>]
|
||||
url <url>
|
||||
file <file>
|
||||
post [<flags>] <url>
|
||||
```
|
||||
-404
@@ -1,404 +0,0 @@
|
||||
package kingpin
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/alecthomas/assert"
|
||||
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func newTestApp() *Application {
|
||||
return New("test", "").Terminate(nil)
|
||||
}
|
||||
|
||||
func TestCommander(t *testing.T) {
|
||||
c := newTestApp()
|
||||
ping := c.Command("ping", "Ping an IP address.")
|
||||
pingTTL := ping.Flag("ttl", "TTL for ICMP packets").Short('t').Default("5s").Duration()
|
||||
|
||||
selected, err := c.Parse([]string{"ping"})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "ping", selected)
|
||||
assert.Equal(t, 5*time.Second, *pingTTL)
|
||||
|
||||
selected, err = c.Parse([]string{"ping", "--ttl=10s"})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "ping", selected)
|
||||
assert.Equal(t, 10*time.Second, *pingTTL)
|
||||
}
|
||||
|
||||
func TestRequiredFlags(t *testing.T) {
|
||||
c := newTestApp()
|
||||
c.Flag("a", "a").String()
|
||||
c.Flag("b", "b").Required().String()
|
||||
|
||||
_, err := c.Parse([]string{"--a=foo"})
|
||||
assert.Error(t, err)
|
||||
_, err = c.Parse([]string{"--b=foo"})
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestRepeatableFlags(t *testing.T) {
|
||||
c := newTestApp()
|
||||
c.Flag("a", "a").String()
|
||||
c.Flag("b", "b").Strings()
|
||||
_, err := c.Parse([]string{"--a=foo", "--a=bar"})
|
||||
assert.Error(t, err)
|
||||
_, err = c.Parse([]string{"--b=foo", "--b=bar"})
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestInvalidDefaultFlagValueErrors(t *testing.T) {
|
||||
c := newTestApp()
|
||||
c.Flag("foo", "foo").Default("a").Int()
|
||||
_, err := c.Parse([]string{})
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestInvalidDefaultArgValueErrors(t *testing.T) {
|
||||
c := newTestApp()
|
||||
cmd := c.Command("cmd", "cmd")
|
||||
cmd.Arg("arg", "arg").Default("one").Int()
|
||||
_, err := c.Parse([]string{"cmd"})
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestArgsRequiredAfterNonRequiredErrors(t *testing.T) {
|
||||
c := newTestApp()
|
||||
cmd := c.Command("cmd", "")
|
||||
cmd.Arg("a", "a").String()
|
||||
cmd.Arg("b", "b").Required().String()
|
||||
_, err := c.Parse([]string{"cmd"})
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestArgsMultipleRequiredThenNonRequired(t *testing.T) {
|
||||
c := newTestApp().Writer(ioutil.Discard)
|
||||
cmd := c.Command("cmd", "")
|
||||
cmd.Arg("a", "a").Required().String()
|
||||
cmd.Arg("b", "b").Required().String()
|
||||
cmd.Arg("c", "c").String()
|
||||
cmd.Arg("d", "d").String()
|
||||
_, err := c.Parse([]string{"cmd", "a", "b"})
|
||||
assert.NoError(t, err)
|
||||
_, err = c.Parse([]string{})
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestDispatchCallbackIsCalled(t *testing.T) {
|
||||
dispatched := false
|
||||
c := newTestApp()
|
||||
c.Command("cmd", "").Action(func(*ParseContext) error {
|
||||
dispatched = true
|
||||
return nil
|
||||
})
|
||||
|
||||
_, err := c.Parse([]string{"cmd"})
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, dispatched)
|
||||
}
|
||||
|
||||
func TestTopLevelArgWorks(t *testing.T) {
|
||||
c := newTestApp()
|
||||
s := c.Arg("arg", "help").String()
|
||||
_, err := c.Parse([]string{"foo"})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "foo", *s)
|
||||
}
|
||||
|
||||
func TestTopLevelArgCantBeUsedWithCommands(t *testing.T) {
|
||||
c := newTestApp()
|
||||
c.Arg("arg", "help").String()
|
||||
c.Command("cmd", "help")
|
||||
_, err := c.Parse([]string{})
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestTooManyArgs(t *testing.T) {
|
||||
a := newTestApp()
|
||||
a.Arg("a", "").String()
|
||||
_, err := a.Parse([]string{"a", "b"})
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestTooManyArgsAfterCommand(t *testing.T) {
|
||||
a := newTestApp()
|
||||
a.Command("a", "")
|
||||
assert.NoError(t, a.init())
|
||||
_, err := a.Parse([]string{"a", "b"})
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestArgsLooksLikeFlagsWithConsumeRemainder(t *testing.T) {
|
||||
a := newTestApp()
|
||||
a.Arg("opts", "").Required().Strings()
|
||||
_, err := a.Parse([]string{"hello", "-world"})
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestCommandParseDoesNotResetFlagsToDefault(t *testing.T) {
|
||||
app := newTestApp()
|
||||
flag := app.Flag("flag", "").Default("default").String()
|
||||
app.Command("cmd", "")
|
||||
|
||||
_, err := app.Parse([]string{"--flag=123", "cmd"})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "123", *flag)
|
||||
}
|
||||
|
||||
func TestCommandParseDoesNotFailRequired(t *testing.T) {
|
||||
app := newTestApp()
|
||||
flag := app.Flag("flag", "").Required().String()
|
||||
app.Command("cmd", "")
|
||||
|
||||
_, err := app.Parse([]string{"cmd", "--flag=123"})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "123", *flag)
|
||||
}
|
||||
|
||||
func TestSelectedCommand(t *testing.T) {
|
||||
app := newTestApp()
|
||||
c0 := app.Command("c0", "")
|
||||
c0.Command("c1", "")
|
||||
s, err := app.Parse([]string{"c0", "c1"})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "c0 c1", s)
|
||||
}
|
||||
|
||||
func TestSubCommandRequired(t *testing.T) {
|
||||
app := newTestApp()
|
||||
c0 := app.Command("c0", "")
|
||||
c0.Command("c1", "")
|
||||
_, err := app.Parse([]string{"c0"})
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestInterspersedFalse(t *testing.T) {
|
||||
app := newTestApp().Interspersed(false)
|
||||
a1 := app.Arg("a1", "").String()
|
||||
a2 := app.Arg("a2", "").String()
|
||||
f1 := app.Flag("flag", "").String()
|
||||
|
||||
_, err := app.Parse([]string{"a1", "--flag=flag"})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "a1", *a1)
|
||||
assert.Equal(t, "--flag=flag", *a2)
|
||||
assert.Equal(t, "", *f1)
|
||||
}
|
||||
|
||||
func TestInterspersedTrue(t *testing.T) {
|
||||
// test once with the default value and once with explicit true
|
||||
for i := 0; i < 2; i++ {
|
||||
app := newTestApp()
|
||||
if i != 0 {
|
||||
t.Log("Setting explicit")
|
||||
app.Interspersed(true)
|
||||
} else {
|
||||
t.Log("Using default")
|
||||
}
|
||||
a1 := app.Arg("a1", "").String()
|
||||
a2 := app.Arg("a2", "").String()
|
||||
f1 := app.Flag("flag", "").String()
|
||||
|
||||
_, err := app.Parse([]string{"a1", "--flag=flag"})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "a1", *a1)
|
||||
assert.Equal(t, "", *a2)
|
||||
assert.Equal(t, "flag", *f1)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDefaultEnvars(t *testing.T) {
|
||||
a := New("some-app", "").Terminate(nil).DefaultEnvars()
|
||||
f0 := a.Flag("some-flag", "")
|
||||
f0.Bool()
|
||||
f1 := a.Flag("some-other-flag", "").NoEnvar()
|
||||
f1.Bool()
|
||||
f2 := a.Flag("a-1-flag", "")
|
||||
f2.Bool()
|
||||
_, err := a.Parse([]string{})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "SOME_APP_SOME_FLAG", f0.envar)
|
||||
assert.Equal(t, "", f1.envar)
|
||||
assert.Equal(t, "SOME_APP_A_1_FLAG", f2.envar)
|
||||
}
|
||||
|
||||
func TestBashCompletionOptionsWithEmptyApp(t *testing.T) {
|
||||
a := newTestApp()
|
||||
context, err := a.ParseContext([]string{"--completion-bash"})
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error whilst parsing context: [%v]", err)
|
||||
}
|
||||
args := a.completionOptions(context)
|
||||
assert.Equal(t, []string(nil), args)
|
||||
}
|
||||
|
||||
func TestBashCompletionOptions(t *testing.T) {
|
||||
a := newTestApp()
|
||||
a.Command("one", "")
|
||||
a.Flag("flag-0", "").String()
|
||||
a.Flag("flag-1", "").HintOptions("opt1", "opt2", "opt3").String()
|
||||
|
||||
two := a.Command("two", "")
|
||||
two.Flag("flag-2", "").String()
|
||||
two.Flag("flag-3", "").HintOptions("opt4", "opt5", "opt6").String()
|
||||
|
||||
three := a.Command("three", "")
|
||||
three.Flag("flag-4", "").String()
|
||||
three.Arg("arg-1", "").String()
|
||||
three.Arg("arg-2", "").HintOptions("arg-2-opt-1", "arg-2-opt-2").String()
|
||||
three.Arg("arg-3", "").String()
|
||||
three.Arg("arg-4", "").HintAction(func() []string {
|
||||
return []string{"arg-4-opt-1", "arg-4-opt-2"}
|
||||
}).String()
|
||||
|
||||
cases := []struct {
|
||||
Args string
|
||||
ExpectedOptions []string
|
||||
}{
|
||||
{
|
||||
Args: "--completion-bash",
|
||||
ExpectedOptions: []string{"help", "one", "three", "two"},
|
||||
},
|
||||
{
|
||||
Args: "--completion-bash --",
|
||||
ExpectedOptions: []string{"--flag-0", "--flag-1", "--help"},
|
||||
},
|
||||
{
|
||||
Args: "--completion-bash --fla",
|
||||
ExpectedOptions: []string{"--flag-0", "--flag-1", "--help"},
|
||||
},
|
||||
{
|
||||
// No options available for flag-0, return to cmd completion
|
||||
Args: "--completion-bash --flag-0",
|
||||
ExpectedOptions: []string{"help", "one", "three", "two"},
|
||||
},
|
||||
{
|
||||
Args: "--completion-bash --flag-0 --",
|
||||
ExpectedOptions: []string{"--flag-0", "--flag-1", "--help"},
|
||||
},
|
||||
{
|
||||
Args: "--completion-bash --flag-1",
|
||||
ExpectedOptions: []string{"opt1", "opt2", "opt3"},
|
||||
},
|
||||
{
|
||||
Args: "--completion-bash --flag-1 opt",
|
||||
ExpectedOptions: []string{"opt1", "opt2", "opt3"},
|
||||
},
|
||||
{
|
||||
Args: "--completion-bash --flag-1 opt1",
|
||||
ExpectedOptions: []string{"help", "one", "three", "two"},
|
||||
},
|
||||
{
|
||||
Args: "--completion-bash --flag-1 opt1 --",
|
||||
ExpectedOptions: []string{"--flag-0", "--flag-1", "--help"},
|
||||
},
|
||||
|
||||
// Try Subcommand
|
||||
{
|
||||
Args: "--completion-bash two",
|
||||
ExpectedOptions: []string(nil),
|
||||
},
|
||||
{
|
||||
Args: "--completion-bash two --",
|
||||
ExpectedOptions: []string{"--help", "--flag-2", "--flag-3", "--flag-0", "--flag-1"},
|
||||
},
|
||||
{
|
||||
Args: "--completion-bash two --flag",
|
||||
ExpectedOptions: []string{"--help", "--flag-2", "--flag-3", "--flag-0", "--flag-1"},
|
||||
},
|
||||
{
|
||||
Args: "--completion-bash two --flag-2",
|
||||
ExpectedOptions: []string(nil),
|
||||
},
|
||||
{
|
||||
// Top level flags carry downwards
|
||||
Args: "--completion-bash two --flag-1",
|
||||
ExpectedOptions: []string{"opt1", "opt2", "opt3"},
|
||||
},
|
||||
{
|
||||
// Top level flags carry downwards
|
||||
Args: "--completion-bash two --flag-1 opt",
|
||||
ExpectedOptions: []string{"opt1", "opt2", "opt3"},
|
||||
},
|
||||
{
|
||||
// Top level flags carry downwards
|
||||
Args: "--completion-bash two --flag-1 opt1",
|
||||
ExpectedOptions: []string(nil),
|
||||
},
|
||||
{
|
||||
Args: "--completion-bash two --flag-3",
|
||||
ExpectedOptions: []string{"opt4", "opt5", "opt6"},
|
||||
},
|
||||
{
|
||||
Args: "--completion-bash two --flag-3 opt",
|
||||
ExpectedOptions: []string{"opt4", "opt5", "opt6"},
|
||||
},
|
||||
{
|
||||
Args: "--completion-bash two --flag-3 opt4",
|
||||
ExpectedOptions: []string(nil),
|
||||
},
|
||||
{
|
||||
Args: "--completion-bash two --flag-3 opt4 --",
|
||||
ExpectedOptions: []string{"--help", "--flag-2", "--flag-3", "--flag-0", "--flag-1"},
|
||||
},
|
||||
|
||||
// Args complete
|
||||
{
|
||||
// After a command with an arg with no options, nothing should be
|
||||
// shown
|
||||
Args: "--completion-bash three ",
|
||||
ExpectedOptions: []string(nil),
|
||||
},
|
||||
{
|
||||
// After a command with an arg, explicitly starting a flag should
|
||||
// complete flags
|
||||
Args: "--completion-bash three --",
|
||||
ExpectedOptions: []string{"--flag-0", "--flag-1", "--flag-4", "--help"},
|
||||
},
|
||||
{
|
||||
// After a command with an arg that does have completions, they
|
||||
// should be shown
|
||||
Args: "--completion-bash three arg1 ",
|
||||
ExpectedOptions: []string{"arg-2-opt-1", "arg-2-opt-2"},
|
||||
},
|
||||
{
|
||||
// After a command with an arg that does have completions, but a
|
||||
// flag is started, flag options should be completed
|
||||
Args: "--completion-bash three arg1 --",
|
||||
ExpectedOptions: []string{"--flag-0", "--flag-1", "--flag-4", "--help"},
|
||||
},
|
||||
{
|
||||
// After a command with an arg that has no completions, and isn't first,
|
||||
// nothing should be shown
|
||||
Args: "--completion-bash three arg1 arg2 ",
|
||||
ExpectedOptions: []string(nil),
|
||||
},
|
||||
{
|
||||
// After a command with a different arg that also has completions,
|
||||
// those different options should be shown
|
||||
Args: "--completion-bash three arg1 arg2 arg3 ",
|
||||
ExpectedOptions: []string{"arg-4-opt-1", "arg-4-opt-2"},
|
||||
},
|
||||
{
|
||||
// After a command with all args listed, nothing should complete
|
||||
Args: "--completion-bash three arg1 arg2 arg3 arg4",
|
||||
ExpectedOptions: []string(nil),
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
context, _ := a.ParseContext(strings.Split(c.Args, " "))
|
||||
args := a.completionOptions(context)
|
||||
|
||||
sort.Strings(args)
|
||||
sort.Strings(c.ExpectedOptions)
|
||||
|
||||
assert.Equal(t, c.ExpectedOptions, args, "Expected != Actual: [%v] != [%v]. \nInput was: [%v]", c.ExpectedOptions, args, c.Args)
|
||||
}
|
||||
|
||||
}
|
||||
-84
@@ -1,84 +0,0 @@
|
||||
package kingpin
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/alecthomas/assert"
|
||||
)
|
||||
|
||||
func TestArgRemainder(t *testing.T) {
|
||||
app := New("test", "")
|
||||
v := app.Arg("test", "").Strings()
|
||||
args := []string{"hello", "world"}
|
||||
_, err := app.Parse(args)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, args, *v)
|
||||
}
|
||||
|
||||
func TestArgRemainderErrorsWhenNotLast(t *testing.T) {
|
||||
a := newArgGroup()
|
||||
a.Arg("test", "").Strings()
|
||||
a.Arg("test2", "").String()
|
||||
assert.Error(t, a.init())
|
||||
}
|
||||
|
||||
func TestArgMultipleRequired(t *testing.T) {
|
||||
terminated := false
|
||||
app := New("test", "")
|
||||
app.Version("0.0.0").Writer(ioutil.Discard)
|
||||
app.Arg("a", "").Required().String()
|
||||
app.Arg("b", "").Required().String()
|
||||
app.Terminate(func(int) { terminated = true })
|
||||
|
||||
_, err := app.Parse([]string{})
|
||||
assert.Error(t, err)
|
||||
_, err = app.Parse([]string{"A"})
|
||||
assert.Error(t, err)
|
||||
_, err = app.Parse([]string{"A", "B"})
|
||||
assert.NoError(t, err)
|
||||
_, err = app.Parse([]string{"--version"})
|
||||
assert.True(t, terminated)
|
||||
}
|
||||
|
||||
func TestInvalidArgsDefaultCanBeOverridden(t *testing.T) {
|
||||
app := New("test", "")
|
||||
app.Arg("a", "").Default("invalid").Bool()
|
||||
_, err := app.Parse([]string{})
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestArgMultipleValuesDefault(t *testing.T) {
|
||||
app := New("test", "")
|
||||
a := app.Arg("a", "").Default("default1", "default2").Strings()
|
||||
_, err := app.Parse([]string{})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, []string{"default1", "default2"}, *a)
|
||||
}
|
||||
|
||||
func TestRequiredArgWithEnvarMissingErrors(t *testing.T) {
|
||||
app := newTestApp()
|
||||
app.Arg("t", "").Envar("TEST_ARG_ENVAR").Required().Int()
|
||||
_, err := app.Parse([]string{})
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestArgRequiredWithEnvar(t *testing.T) {
|
||||
os.Setenv("TEST_ARG_ENVAR", "123")
|
||||
app := newTestApp()
|
||||
flag := app.Arg("t", "").Envar("TEST_ARG_ENVAR").Required().Int()
|
||||
_, err := app.Parse([]string{})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 123, *flag)
|
||||
}
|
||||
|
||||
func TestSubcommandArgRequiredWithEnvar(t *testing.T) {
|
||||
os.Setenv("TEST_ARG_ENVAR", "123")
|
||||
app := newTestApp()
|
||||
cmd := app.Command("command", "")
|
||||
flag := cmd.Arg("t", "").Envar("TEST_ARG_ENVAR").Required().Int()
|
||||
_, err := app.Parse([]string{"command"})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 123, *flag)
|
||||
}
|
||||
-374
@@ -1,374 +0,0 @@
|
||||
package kingpin
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/alecthomas/assert"
|
||||
|
||||
"testing"
|
||||
)
|
||||
|
||||
func parseAndExecute(app *Application, context *ParseContext) (string, error) {
|
||||
if err := parse(context, app); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
selected, err := app.setValues(context)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return app.execute(context, selected)
|
||||
}
|
||||
|
||||
func complete(t *testing.T, app *Application, args ...string) []string {
|
||||
context, err := app.ParseContext(args)
|
||||
assert.NoError(t, err)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
completions := app.completionOptions(context)
|
||||
sort.Strings(completions)
|
||||
|
||||
return completions
|
||||
}
|
||||
|
||||
func TestNestedCommands(t *testing.T) {
|
||||
app := New("app", "")
|
||||
sub1 := app.Command("sub1", "")
|
||||
sub1.Flag("sub1", "")
|
||||
subsub1 := sub1.Command("sub1sub1", "")
|
||||
subsub1.Command("sub1sub1end", "")
|
||||
|
||||
sub2 := app.Command("sub2", "")
|
||||
sub2.Flag("sub2", "")
|
||||
sub2.Command("sub2sub1", "")
|
||||
|
||||
context := tokenize([]string{"sub1", "sub1sub1", "sub1sub1end"}, false)
|
||||
selected, err := parseAndExecute(app, context)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, context.EOL())
|
||||
assert.Equal(t, "sub1 sub1sub1 sub1sub1end", selected)
|
||||
}
|
||||
|
||||
func TestNestedCommandsWithArgs(t *testing.T) {
|
||||
app := New("app", "")
|
||||
cmd := app.Command("a", "").Command("b", "")
|
||||
a := cmd.Arg("a", "").String()
|
||||
b := cmd.Arg("b", "").String()
|
||||
context := tokenize([]string{"a", "b", "c", "d"}, false)
|
||||
selected, err := parseAndExecute(app, context)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, context.EOL())
|
||||
assert.Equal(t, "a b", selected)
|
||||
assert.Equal(t, "c", *a)
|
||||
assert.Equal(t, "d", *b)
|
||||
}
|
||||
|
||||
func TestNestedCommandsWithFlags(t *testing.T) {
|
||||
app := New("app", "")
|
||||
cmd := app.Command("a", "").Command("b", "")
|
||||
a := cmd.Flag("aaa", "").Short('a').String()
|
||||
b := cmd.Flag("bbb", "").Short('b').String()
|
||||
err := app.init()
|
||||
assert.NoError(t, err)
|
||||
context := tokenize(strings.Split("a b --aaa x -b x", " "), false)
|
||||
selected, err := parseAndExecute(app, context)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, context.EOL())
|
||||
assert.Equal(t, "a b", selected)
|
||||
assert.Equal(t, "x", *a)
|
||||
assert.Equal(t, "x", *b)
|
||||
}
|
||||
|
||||
func TestNestedCommandWithMergedFlags(t *testing.T) {
|
||||
app := New("app", "")
|
||||
cmd0 := app.Command("a", "")
|
||||
cmd0f0 := cmd0.Flag("aflag", "").Bool()
|
||||
// cmd1 := app.Command("b", "")
|
||||
// cmd1f0 := cmd0.Flag("bflag", "").Bool()
|
||||
cmd00 := cmd0.Command("aa", "")
|
||||
cmd00f0 := cmd00.Flag("aaflag", "").Bool()
|
||||
err := app.init()
|
||||
assert.NoError(t, err)
|
||||
context := tokenize(strings.Split("a aa --aflag --aaflag", " "), false)
|
||||
selected, err := parseAndExecute(app, context)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, *cmd0f0)
|
||||
assert.True(t, *cmd00f0)
|
||||
assert.Equal(t, "a aa", selected)
|
||||
}
|
||||
|
||||
func TestNestedCommandWithDuplicateFlagErrors(t *testing.T) {
|
||||
app := New("app", "")
|
||||
app.Flag("test", "").Bool()
|
||||
app.Command("cmd0", "").Flag("test", "").Bool()
|
||||
err := app.init()
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestNestedCommandWithArgAndMergedFlags(t *testing.T) {
|
||||
app := New("app", "")
|
||||
cmd0 := app.Command("a", "")
|
||||
cmd0f0 := cmd0.Flag("aflag", "").Bool()
|
||||
// cmd1 := app.Command("b", "")
|
||||
// cmd1f0 := cmd0.Flag("bflag", "").Bool()
|
||||
cmd00 := cmd0.Command("aa", "")
|
||||
cmd00a0 := cmd00.Arg("arg", "").String()
|
||||
cmd00f0 := cmd00.Flag("aaflag", "").Bool()
|
||||
err := app.init()
|
||||
assert.NoError(t, err)
|
||||
context := tokenize(strings.Split("a aa hello --aflag --aaflag", " "), false)
|
||||
selected, err := parseAndExecute(app, context)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, *cmd0f0)
|
||||
assert.True(t, *cmd00f0)
|
||||
assert.Equal(t, "a aa", selected)
|
||||
assert.Equal(t, "hello", *cmd00a0)
|
||||
}
|
||||
|
||||
func TestDefaultSubcommandEOL(t *testing.T) {
|
||||
app := newTestApp()
|
||||
c0 := app.Command("c0", "").Default()
|
||||
c0.Command("c01", "").Default()
|
||||
c0.Command("c02", "")
|
||||
|
||||
cmd, err := app.Parse([]string{"c0"})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "c0 c01", cmd)
|
||||
}
|
||||
|
||||
func TestDefaultSubcommandWithArg(t *testing.T) {
|
||||
app := newTestApp()
|
||||
c0 := app.Command("c0", "").Default()
|
||||
c01 := c0.Command("c01", "").Default()
|
||||
c012 := c01.Command("c012", "").Default()
|
||||
a0 := c012.Arg("a0", "").String()
|
||||
c0.Command("c02", "")
|
||||
|
||||
cmd, err := app.Parse([]string{"c0", "hello"})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "c0 c01 c012", cmd)
|
||||
assert.Equal(t, "hello", *a0)
|
||||
}
|
||||
|
||||
func TestDefaultSubcommandWithFlags(t *testing.T) {
|
||||
app := newTestApp()
|
||||
c0 := app.Command("c0", "").Default()
|
||||
_ = c0.Flag("f0", "").Int()
|
||||
c0c1 := c0.Command("c1", "").Default()
|
||||
c0c1f1 := c0c1.Flag("f1", "").Int()
|
||||
selected, err := app.Parse([]string{"--f1=2"})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "c0 c1", selected)
|
||||
assert.Equal(t, 2, *c0c1f1)
|
||||
_, err = app.Parse([]string{"--f2"})
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestMultipleDefaultCommands(t *testing.T) {
|
||||
app := newTestApp()
|
||||
app.Command("c0", "").Default()
|
||||
app.Command("c1", "").Default()
|
||||
_, err := app.Parse([]string{})
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestAliasedCommand(t *testing.T) {
|
||||
app := newTestApp()
|
||||
app.Command("one", "").Alias("two")
|
||||
selected, _ := app.Parse([]string{"one"})
|
||||
assert.Equal(t, "one", selected)
|
||||
selected, _ = app.Parse([]string{"two"})
|
||||
assert.Equal(t, "one", selected)
|
||||
// 2 due to "help" and "one"
|
||||
assert.Equal(t, 2, len(app.Model().FlattenedCommands()))
|
||||
}
|
||||
|
||||
func TestDuplicateAlias(t *testing.T) {
|
||||
app := newTestApp()
|
||||
app.Command("one", "")
|
||||
app.Command("two", "").Alias("one")
|
||||
_, err := app.Parse([]string{"one"})
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestFlagCompletion(t *testing.T) {
|
||||
app := newTestApp()
|
||||
app.Command("one", "")
|
||||
two := app.Command("two", "")
|
||||
two.Flag("flag-1", "")
|
||||
two.Flag("flag-2", "").HintOptions("opt1", "opt2", "opt3")
|
||||
two.Flag("flag-3", "")
|
||||
|
||||
cases := []struct {
|
||||
target cmdMixin
|
||||
flagName string
|
||||
flagValue string
|
||||
expectedFlagMatch bool
|
||||
expectedOptionMatch bool
|
||||
expectedFlags []string
|
||||
}{
|
||||
{
|
||||
// Test top level flags
|
||||
target: app.cmdMixin,
|
||||
flagName: "",
|
||||
flagValue: "",
|
||||
expectedFlagMatch: false,
|
||||
expectedOptionMatch: false,
|
||||
expectedFlags: []string{"--help"},
|
||||
},
|
||||
{
|
||||
// Test no flag passed
|
||||
target: two.cmdMixin,
|
||||
flagName: "",
|
||||
flagValue: "",
|
||||
expectedFlagMatch: false,
|
||||
expectedOptionMatch: false,
|
||||
expectedFlags: []string{"--flag-1", "--flag-2", "--flag-3"},
|
||||
},
|
||||
{
|
||||
// Test an incomplete flag. Should still give all options as if the flag wasn't given at all.
|
||||
target: two.cmdMixin,
|
||||
flagName: "flag-",
|
||||
flagValue: "",
|
||||
expectedFlagMatch: false,
|
||||
expectedOptionMatch: false,
|
||||
expectedFlags: []string{"--flag-1", "--flag-2", "--flag-3"},
|
||||
},
|
||||
{
|
||||
// Test with a complete flag. Should show available choices for the flag
|
||||
// This flag has no options. No options should be produced.
|
||||
// Should also report an option was matched
|
||||
target: two.cmdMixin,
|
||||
flagName: "flag-1",
|
||||
flagValue: "",
|
||||
expectedFlagMatch: true,
|
||||
expectedOptionMatch: true,
|
||||
expectedFlags: []string(nil),
|
||||
},
|
||||
{
|
||||
// Test with a complete flag. Should show available choices for the flag
|
||||
target: two.cmdMixin,
|
||||
flagName: "flag-2",
|
||||
flagValue: "",
|
||||
expectedFlagMatch: true,
|
||||
expectedOptionMatch: false,
|
||||
expectedFlags: []string{"opt1", "opt2", "opt3"},
|
||||
},
|
||||
{
|
||||
// Test with a complete flag and complete option for that flag.
|
||||
target: two.cmdMixin,
|
||||
flagName: "flag-2",
|
||||
flagValue: "opt1",
|
||||
expectedFlagMatch: true,
|
||||
expectedOptionMatch: true,
|
||||
expectedFlags: []string{"opt1", "opt2", "opt3"},
|
||||
},
|
||||
}
|
||||
|
||||
for i, c := range cases {
|
||||
choices, flagMatch, optionMatch := c.target.FlagCompletion(c.flagName, c.flagValue)
|
||||
assert.Equal(t, c.expectedFlags, choices, "Test case %d: expectedFlags != actual flags", i+1)
|
||||
assert.Equal(t, c.expectedFlagMatch, flagMatch, "Test case %d: expectedFlagMatch != flagMatch", i+1)
|
||||
assert.Equal(t, c.expectedOptionMatch, optionMatch, "Test case %d: expectedOptionMatch != optionMatch", i+1)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestCmdCompletion(t *testing.T) {
|
||||
app := newTestApp()
|
||||
app.Command("one", "")
|
||||
two := app.Command("two", "")
|
||||
two.Command("sub1", "")
|
||||
two.Command("sub2", "")
|
||||
|
||||
assert.Equal(t, []string{"help", "one", "two"}, complete(t, app))
|
||||
assert.Equal(t, []string{"sub1", "sub2"}, complete(t, app, "two"))
|
||||
}
|
||||
|
||||
func TestHiddenCmdCompletion(t *testing.T) {
|
||||
app := newTestApp()
|
||||
|
||||
// top level visible & hidden cmds, with no sub-cmds
|
||||
app.Command("visible1", "")
|
||||
app.Command("hidden1", "").Hidden()
|
||||
|
||||
// visible cmd with visible & hidden sub-cmds
|
||||
visible2 := app.Command("visible2", "")
|
||||
visible2.Command("visible2-visible", "")
|
||||
visible2.Command("visible2-hidden", "").Hidden()
|
||||
|
||||
// hidden cmd with visible & hidden sub-cmds
|
||||
hidden2 := app.Command("hidden2", "").Hidden()
|
||||
hidden2.Command("hidden2-visible", "")
|
||||
hidden2.Command("hidden2-hidden", "").Hidden()
|
||||
|
||||
// Only top level visible cmds should show
|
||||
assert.Equal(t, []string{"help", "visible1", "visible2"}, complete(t, app))
|
||||
|
||||
// Only visible sub-cmds should show
|
||||
assert.Equal(t, []string{"visible2-visible"}, complete(t, app, "visible2"))
|
||||
|
||||
// Hidden commands should still complete visible sub-cmds
|
||||
assert.Equal(t, []string{"hidden2-visible"}, complete(t, app, "hidden2"))
|
||||
}
|
||||
|
||||
func TestDefaultCmdCompletion(t *testing.T) {
|
||||
app := newTestApp()
|
||||
|
||||
cmd1 := app.Command("cmd1", "")
|
||||
|
||||
cmd1Sub1 := cmd1.Command("cmd1-sub1", "")
|
||||
cmd1Sub1.Arg("cmd1-sub1-arg1", "").HintOptions("cmd1-arg1").String()
|
||||
|
||||
cmd2 := app.Command("cmd2", "").Default()
|
||||
|
||||
cmd2.Command("cmd2-sub1", "")
|
||||
|
||||
cmd2Sub2 := cmd2.Command("cmd2-sub2", "").Default()
|
||||
|
||||
cmd2Sub2Sub1 := cmd2Sub2.Command("cmd2-sub2-sub1", "").Default()
|
||||
cmd2Sub2Sub1.Arg("cmd2-sub2-sub1-arg1", "").HintOptions("cmd2-sub2-sub1-arg1").String()
|
||||
cmd2Sub2Sub1.Arg("cmd2-sub2-sub1-arg2", "").HintOptions("cmd2-sub2-sub1-arg2").String()
|
||||
|
||||
// Without args, should get:
|
||||
// - root cmds (including implicit "help")
|
||||
// - thread of default cmds
|
||||
// - first arg hints for the final default cmd
|
||||
assert.Equal(t, []string{"cmd1", "cmd2", "cmd2-sub1", "cmd2-sub2", "cmd2-sub2-sub1", "cmd2-sub2-sub1-arg1", "help"}, complete(t, app))
|
||||
|
||||
// With a non-default cmd already listed, should get:
|
||||
// - sub cmds of that arg
|
||||
assert.Equal(t, []string{"cmd1-sub1"}, complete(t, app, "cmd1"))
|
||||
|
||||
// With an explicit default cmd listed, should get:
|
||||
// - default child-cmds
|
||||
// - first arg hints for the final default cmd
|
||||
assert.Equal(t, []string{"cmd2-sub1", "cmd2-sub2", "cmd2-sub2-sub1", "cmd2-sub2-sub1-arg1"}, complete(t, app, "cmd2"))
|
||||
|
||||
// Args should be completed when all preceding cmds are explicit, and when
|
||||
// any of them are implicit (not listed). Check this by trying all possible
|
||||
// combinations of choosing/excluding the three levels of cmds. This tests
|
||||
// root-level default, middle default, and end default.
|
||||
for i := 0; i < 8; i++ {
|
||||
var cmdline []string
|
||||
|
||||
if i&1 != 0 {
|
||||
cmdline = append(cmdline, "cmd2")
|
||||
}
|
||||
if i&2 != 0 {
|
||||
cmdline = append(cmdline, "cmd2-sub2")
|
||||
}
|
||||
if i&4 != 0 {
|
||||
cmdline = append(cmdline, "cmd2-sub2-sub1")
|
||||
}
|
||||
|
||||
assert.Contains(t, complete(t, app, cmdline...), "cmd2-sub2-sub1-arg1", "with cmdline: %v", cmdline)
|
||||
}
|
||||
|
||||
// With both args of a default sub cmd, should get no completions
|
||||
assert.Empty(t, complete(t, app, "arg1", "arg2"))
|
||||
}
|
||||
-78
@@ -1,78 +0,0 @@
|
||||
package kingpin
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/alecthomas/assert"
|
||||
)
|
||||
|
||||
func TestResolveWithBuiltin(t *testing.T) {
|
||||
a := completionsMixin{}
|
||||
|
||||
hintAction1 := func() []string {
|
||||
return []string{"opt1", "opt2"}
|
||||
}
|
||||
hintAction2 := func() []string {
|
||||
return []string{"opt3", "opt4"}
|
||||
}
|
||||
|
||||
a.builtinHintActions = []HintAction{hintAction1, hintAction2}
|
||||
|
||||
args := a.resolveCompletions()
|
||||
assert.Equal(t, []string{"opt1", "opt2", "opt3", "opt4"}, args)
|
||||
}
|
||||
|
||||
func TestResolveWithUser(t *testing.T) {
|
||||
a := completionsMixin{}
|
||||
hintAction1 := func() []string {
|
||||
return []string{"opt1", "opt2"}
|
||||
}
|
||||
hintAction2 := func() []string {
|
||||
return []string{"opt3", "opt4"}
|
||||
}
|
||||
|
||||
a.hintActions = []HintAction{hintAction1, hintAction2}
|
||||
|
||||
args := a.resolveCompletions()
|
||||
assert.Equal(t, []string{"opt1", "opt2", "opt3", "opt4"}, args)
|
||||
}
|
||||
|
||||
func TestResolveWithCombination(t *testing.T) {
|
||||
a := completionsMixin{}
|
||||
builtin := func() []string {
|
||||
return []string{"opt1", "opt2"}
|
||||
}
|
||||
user := func() []string {
|
||||
return []string{"opt3", "opt4"}
|
||||
}
|
||||
|
||||
a.builtinHintActions = []HintAction{builtin}
|
||||
a.hintActions = []HintAction{user}
|
||||
|
||||
args := a.resolveCompletions()
|
||||
// User provided args take preference over builtin (enum-defined) args.
|
||||
assert.Equal(t, []string{"opt3", "opt4"}, args)
|
||||
}
|
||||
|
||||
func TestAddHintAction(t *testing.T) {
|
||||
a := completionsMixin{}
|
||||
hintFunc := func() []string {
|
||||
return []string{"opt1", "opt2"}
|
||||
}
|
||||
a.addHintAction(hintFunc)
|
||||
|
||||
args := a.resolveCompletions()
|
||||
assert.Equal(t, []string{"opt1", "opt2"}, args)
|
||||
}
|
||||
|
||||
func TestAddHintActionBuiltin(t *testing.T) {
|
||||
a := completionsMixin{}
|
||||
hintFunc := func() []string {
|
||||
return []string{"opt1", "opt2"}
|
||||
}
|
||||
|
||||
a.addHintActionBuiltin(hintFunc)
|
||||
|
||||
args := a.resolveCompletions()
|
||||
assert.Equal(t, []string{"opt1", "opt2"}, args)
|
||||
}
|
||||
-46
@@ -1,46 +0,0 @@
|
||||
package kingpin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type HTTPHeaderValue http.Header
|
||||
|
||||
func (h *HTTPHeaderValue) Set(value string) error {
|
||||
parts := strings.SplitN(value, ":", 2)
|
||||
if len(parts) != 2 {
|
||||
return fmt.Errorf("expected HEADER:VALUE got '%s'", value)
|
||||
}
|
||||
(*http.Header)(h).Add(parts[0], parts[1])
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *HTTPHeaderValue) Get() interface{} {
|
||||
return (http.Header)(*h)
|
||||
}
|
||||
|
||||
func (h *HTTPHeaderValue) String() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func HTTPHeader(s Settings) (target *http.Header) {
|
||||
target = new(http.Header)
|
||||
s.SetValue((*HTTPHeaderValue)(target))
|
||||
return
|
||||
}
|
||||
|
||||
// This example ilustrates how to define custom parsers. HTTPHeader
|
||||
// cumulatively parses each encountered --header flag into a http.Header struct.
|
||||
func ExampleValue() {
|
||||
var (
|
||||
curl = New("curl", "transfer a URL")
|
||||
headers = HTTPHeader(curl.Flag("headers", "Add HTTP headers to the request.").Short('H').PlaceHolder("HEADER:VALUE"))
|
||||
)
|
||||
|
||||
curl.Parse([]string{"-H Content-Type:application/octet-stream"})
|
||||
for key, value := range *headers {
|
||||
fmt.Printf("%s = %s\n", key, value)
|
||||
}
|
||||
}
|
||||
-368
@@ -1,368 +0,0 @@
|
||||
package kingpin
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"github.com/alecthomas/assert"
|
||||
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestBool(t *testing.T) {
|
||||
app := newTestApp()
|
||||
b := app.Flag("b", "").Bool()
|
||||
_, err := app.Parse([]string{"--b"})
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, *b)
|
||||
}
|
||||
|
||||
func TestNoBool(t *testing.T) {
|
||||
fg := newFlagGroup()
|
||||
f := fg.Flag("b", "").Default("true")
|
||||
b := f.Bool()
|
||||
fg.init("")
|
||||
tokens := tokenize([]string{"--no-b"}, false)
|
||||
_, err := fg.parse(tokens)
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, *b)
|
||||
}
|
||||
|
||||
func TestNegateNonBool(t *testing.T) {
|
||||
fg := newFlagGroup()
|
||||
f := fg.Flag("b", "")
|
||||
f.Int()
|
||||
fg.init("")
|
||||
tokens := tokenize([]string{"--no-b"}, false)
|
||||
_, err := fg.parse(tokens)
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestNegativePrefixLongFlag(t *testing.T) {
|
||||
fg := newFlagGroup()
|
||||
f := fg.Flag("no-comment", "")
|
||||
b := f.Bool()
|
||||
fg.init("")
|
||||
tokens := tokenize([]string{"--no-comment"}, false)
|
||||
_, err := fg.parse(tokens)
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, *b)
|
||||
}
|
||||
|
||||
func TestInvalidFlagDefaultCanBeOverridden(t *testing.T) {
|
||||
app := newTestApp()
|
||||
app.Flag("a", "").Default("invalid").Bool()
|
||||
_, err := app.Parse([]string{})
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestRequiredFlag(t *testing.T) {
|
||||
app := newTestApp()
|
||||
app.Version("0.0.0").Writer(ioutil.Discard)
|
||||
exits := 0
|
||||
app.Terminate(func(int) { exits++ })
|
||||
app.Flag("a", "").Required().Bool()
|
||||
_, err := app.Parse([]string{"--a"})
|
||||
assert.NoError(t, err)
|
||||
_, err = app.Parse([]string{})
|
||||
assert.Error(t, err)
|
||||
_, err = app.Parse([]string{"--version"})
|
||||
assert.Equal(t, 1, exits)
|
||||
}
|
||||
|
||||
func TestShortFlag(t *testing.T) {
|
||||
app := newTestApp()
|
||||
f := app.Flag("long", "").Short('s').Bool()
|
||||
_, err := app.Parse([]string{"-s"})
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, *f)
|
||||
}
|
||||
|
||||
func TestUnicodeShortFlag(t *testing.T) {
|
||||
app := newTestApp()
|
||||
f := app.Flag("aaa", "").Short('ä').Bool()
|
||||
_, err := app.Parse([]string{"-ä"})
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, *f)
|
||||
}
|
||||
|
||||
func TestCombinedShortFlags(t *testing.T) {
|
||||
app := newTestApp()
|
||||
a := app.Flag("short0", "").Short('0').Bool()
|
||||
b := app.Flag("short1", "").Short('1').Bool()
|
||||
c := app.Flag("short2", "").Short('2').Bool()
|
||||
_, err := app.Parse([]string{"-01"})
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, *a)
|
||||
assert.True(t, *b)
|
||||
assert.False(t, *c)
|
||||
}
|
||||
|
||||
func TestCombinedUnicodeShortFlags(t *testing.T) {
|
||||
app := newTestApp()
|
||||
a := app.Flag("short0", "").Short('0').Bool()
|
||||
b := app.Flag("short1", "").Short('1').Bool()
|
||||
c := app.Flag("short2", "").Short('ä').Bool()
|
||||
d := app.Flag("short3", "").Short('2').Bool()
|
||||
_, err := app.Parse([]string{"-0ä1"})
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, *a)
|
||||
assert.True(t, *b)
|
||||
assert.True(t, *c)
|
||||
assert.False(t, *d)
|
||||
}
|
||||
|
||||
func TestCombinedShortFlagArg(t *testing.T) {
|
||||
a := newTestApp()
|
||||
n := a.Flag("short", "").Short('s').Int()
|
||||
_, err := a.Parse([]string{"-s10"})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 10, *n)
|
||||
}
|
||||
|
||||
func TestCombinedUnicodeShortFlagArg(t *testing.T) {
|
||||
app := newTestApp()
|
||||
a := app.Flag("short", "").Short('ä').Int()
|
||||
_, err := app.Parse([]string{"-ä10"})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 10, *a)
|
||||
}
|
||||
|
||||
func TestCombinedUnicodeShortFlagUnicodeArg(t *testing.T) {
|
||||
app := newTestApp()
|
||||
a := app.Flag("short", "").Short('ä').String()
|
||||
_, err := app.Parse([]string{"-äöö"})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "öö", *a)
|
||||
}
|
||||
|
||||
func TestEmptyShortFlagIsAnError(t *testing.T) {
|
||||
_, err := newTestApp().Parse([]string{"-"})
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestRequiredWithEnvarMissingErrors(t *testing.T) {
|
||||
app := newTestApp()
|
||||
app.Flag("t", "").OverrideDefaultFromEnvar("TEST_ENVAR").Required().Int()
|
||||
_, err := app.Parse([]string{})
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestRequiredWithEnvar(t *testing.T) {
|
||||
os.Setenv("TEST_ENVAR", "123")
|
||||
app := newTestApp()
|
||||
flag := app.Flag("t", "").Envar("TEST_ENVAR").Required().Int()
|
||||
_, err := app.Parse([]string{})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 123, *flag)
|
||||
}
|
||||
|
||||
func TestSubcommandFlagRequiredWithEnvar(t *testing.T) {
|
||||
os.Setenv("TEST_ENVAR", "123")
|
||||
app := newTestApp()
|
||||
cmd := app.Command("command", "")
|
||||
flag := cmd.Flag("t", "").Envar("TEST_ENVAR").Required().Int()
|
||||
_, err := app.Parse([]string{"command"})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 123, *flag)
|
||||
}
|
||||
|
||||
func TestRegexp(t *testing.T) {
|
||||
app := newTestApp()
|
||||
flag := app.Flag("reg", "").Regexp()
|
||||
_, err := app.Parse([]string{"--reg", "^abc$"})
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, *flag)
|
||||
assert.Equal(t, "^abc$", (*flag).String())
|
||||
assert.Regexp(t, *flag, "abc")
|
||||
assert.NotRegexp(t, *flag, "abcd")
|
||||
}
|
||||
|
||||
func TestDuplicateShortFlag(t *testing.T) {
|
||||
app := newTestApp()
|
||||
app.Flag("a", "").Short('a').String()
|
||||
app.Flag("b", "").Short('a').String()
|
||||
_, err := app.Parse([]string{})
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestDuplicateLongFlag(t *testing.T) {
|
||||
app := newTestApp()
|
||||
app.Flag("a", "").String()
|
||||
app.Flag("a", "").String()
|
||||
_, err := app.Parse([]string{})
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestGetFlagAndOverrideDefault(t *testing.T) {
|
||||
app := newTestApp()
|
||||
a := app.Flag("a", "").Default("default").String()
|
||||
_, err := app.Parse([]string{})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "default", *a)
|
||||
app.GetFlag("a").Default("new")
|
||||
_, err = app.Parse([]string{})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "new", *a)
|
||||
}
|
||||
|
||||
func TestEnvarOverrideDefault(t *testing.T) {
|
||||
os.Setenv("TEST_ENVAR", "123")
|
||||
app := newTestApp()
|
||||
flag := app.Flag("t", "").Default("default").Envar("TEST_ENVAR").String()
|
||||
_, err := app.Parse([]string{})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "123", *flag)
|
||||
}
|
||||
|
||||
func TestFlagMultipleValuesDefault(t *testing.T) {
|
||||
app := newTestApp()
|
||||
a := app.Flag("a", "").Default("default1", "default2").Strings()
|
||||
_, err := app.Parse([]string{})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, []string{"default1", "default2"}, *a)
|
||||
}
|
||||
|
||||
func TestFlagMultipleValuesDefaultNonRepeatable(t *testing.T) {
|
||||
c := newTestApp()
|
||||
c.Flag("foo", "foo").Default("a", "b").String()
|
||||
_, err := c.Parse([]string{})
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestFlagMultipleValuesDefaultEnvarUnix(t *testing.T) {
|
||||
app := newTestApp()
|
||||
a := app.Flag("a", "").Envar("TEST_MULTIPLE_VALUES").Strings()
|
||||
os.Setenv("TEST_MULTIPLE_VALUES", "123\n456\n")
|
||||
_, err := app.Parse([]string{})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, []string{"123", "456"}, *a)
|
||||
}
|
||||
|
||||
func TestFlagMultipleValuesDefaultEnvarWindows(t *testing.T) {
|
||||
app := newTestApp()
|
||||
a := app.Flag("a", "").Envar("TEST_MULTIPLE_VALUES").Strings()
|
||||
os.Setenv("TEST_MULTIPLE_VALUES", "123\r\n456\r\n")
|
||||
_, err := app.Parse([]string{})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, []string{"123", "456"}, *a)
|
||||
}
|
||||
|
||||
func TestFlagMultipleValuesDefaultEnvarNonRepeatable(t *testing.T) {
|
||||
c := newTestApp()
|
||||
a := c.Flag("foo", "foo").Envar("TEST_MULTIPLE_VALUES_NON_REPEATABLE").String()
|
||||
os.Setenv("TEST_MULTIPLE_VALUES_NON_REPEATABLE", "123\n456")
|
||||
_, err := c.Parse([]string{})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "123\n456", *a)
|
||||
}
|
||||
|
||||
func TestFlagHintAction(t *testing.T) {
|
||||
c := newTestApp()
|
||||
|
||||
action := func() []string {
|
||||
return []string{"opt1", "opt2"}
|
||||
}
|
||||
|
||||
a := c.Flag("foo", "foo").HintAction(action)
|
||||
args := a.resolveCompletions()
|
||||
assert.Equal(t, []string{"opt1", "opt2"}, args)
|
||||
}
|
||||
|
||||
func TestFlagHintOptions(t *testing.T) {
|
||||
c := newTestApp()
|
||||
|
||||
a := c.Flag("foo", "foo").HintOptions("opt1", "opt2")
|
||||
args := a.resolveCompletions()
|
||||
assert.Equal(t, []string{"opt1", "opt2"}, args)
|
||||
}
|
||||
|
||||
func TestFlagEnumVar(t *testing.T) {
|
||||
c := newTestApp()
|
||||
var bar string
|
||||
|
||||
a := c.Flag("foo", "foo")
|
||||
a.Enum("opt1", "opt2")
|
||||
b := c.Flag("bar", "bar")
|
||||
b.EnumVar(&bar, "opt3", "opt4")
|
||||
|
||||
args := a.resolveCompletions()
|
||||
assert.Equal(t, []string{"opt1", "opt2"}, args)
|
||||
|
||||
args = b.resolveCompletions()
|
||||
assert.Equal(t, []string{"opt3", "opt4"}, args)
|
||||
}
|
||||
|
||||
func TestMultiHintOptions(t *testing.T) {
|
||||
c := newTestApp()
|
||||
|
||||
a := c.Flag("foo", "foo").HintOptions("opt1").HintOptions("opt2")
|
||||
args := a.resolveCompletions()
|
||||
assert.Equal(t, []string{"opt1", "opt2"}, args)
|
||||
}
|
||||
func TestMultiHintActions(t *testing.T) {
|
||||
c := newTestApp()
|
||||
|
||||
a := c.Flag("foo", "foo").
|
||||
HintAction(func() []string {
|
||||
return []string{"opt1"}
|
||||
}).
|
||||
HintAction(func() []string {
|
||||
return []string{"opt2"}
|
||||
})
|
||||
args := a.resolveCompletions()
|
||||
assert.Equal(t, []string{"opt1", "opt2"}, args)
|
||||
}
|
||||
|
||||
func TestCombinationHintActionsOptions(t *testing.T) {
|
||||
c := newTestApp()
|
||||
|
||||
a := c.Flag("foo", "foo").HintAction(func() []string {
|
||||
return []string{"opt1"}
|
||||
}).HintOptions("opt2")
|
||||
args := a.resolveCompletions()
|
||||
assert.Equal(t, []string{"opt1", "opt2"}, args)
|
||||
}
|
||||
|
||||
func TestCombinationEnumActions(t *testing.T) {
|
||||
c := newTestApp()
|
||||
var foo string
|
||||
|
||||
a := c.Flag("foo", "foo").
|
||||
HintAction(func() []string {
|
||||
return []string{"opt1", "opt2"}
|
||||
})
|
||||
a.Enum("opt3", "opt4")
|
||||
|
||||
b := c.Flag("bar", "bar").
|
||||
HintAction(func() []string {
|
||||
return []string{"opt5", "opt6"}
|
||||
})
|
||||
b.EnumVar(&foo, "opt3", "opt4")
|
||||
|
||||
// Provided HintActions should override automatically generated Enum options.
|
||||
args := a.resolveCompletions()
|
||||
assert.Equal(t, []string{"opt1", "opt2"}, args)
|
||||
|
||||
args = b.resolveCompletions()
|
||||
assert.Equal(t, []string{"opt5", "opt6"}, args)
|
||||
}
|
||||
|
||||
func TestCombinationEnumOptions(t *testing.T) {
|
||||
c := newTestApp()
|
||||
var foo string
|
||||
|
||||
a := c.Flag("foo", "foo").HintOptions("opt1", "opt2")
|
||||
a.Enum("opt3", "opt4")
|
||||
|
||||
b := c.Flag("bar", "bar").HintOptions("opt5", "opt6")
|
||||
b.EnumVar(&foo, "opt3", "opt4")
|
||||
|
||||
// Provided HintOptions should override automatically generated Enum options.
|
||||
args := a.resolveCompletions()
|
||||
assert.Equal(t, []string{"opt1", "opt2"}, args)
|
||||
|
||||
args = b.resolveCompletions()
|
||||
assert.Equal(t, []string{"opt5", "opt6"}, args)
|
||||
|
||||
}
|
||||
-122
@@ -1,122 +0,0 @@
|
||||
package kingpin
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/alecthomas/assert"
|
||||
)
|
||||
|
||||
func TestParserExpandFromFile(t *testing.T) {
|
||||
f, err := ioutil.TempFile("", "")
|
||||
assert.NoError(t, err)
|
||||
defer os.Remove(f.Name())
|
||||
f.WriteString("hello\nworld\n")
|
||||
f.Close()
|
||||
|
||||
app := New("test", "")
|
||||
arg0 := app.Arg("arg0", "").String()
|
||||
arg1 := app.Arg("arg1", "").String()
|
||||
|
||||
_, err = app.Parse([]string{"@" + f.Name()})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "hello", *arg0)
|
||||
assert.Equal(t, "world", *arg1)
|
||||
}
|
||||
|
||||
func TestParserExpandFromFileLeadingArg(t *testing.T) {
|
||||
f, err := ioutil.TempFile("", "")
|
||||
assert.NoError(t, err)
|
||||
defer os.Remove(f.Name())
|
||||
f.WriteString("hello\nworld\n")
|
||||
f.Close()
|
||||
|
||||
app := New("test", "")
|
||||
arg0 := app.Arg("arg0", "").String()
|
||||
arg1 := app.Arg("arg1", "").String()
|
||||
arg2 := app.Arg("arg2", "").String()
|
||||
|
||||
_, err = app.Parse([]string{"prefix", "@" + f.Name()})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "prefix", *arg0)
|
||||
assert.Equal(t, "hello", *arg1)
|
||||
assert.Equal(t, "world", *arg2)
|
||||
}
|
||||
|
||||
func TestParserExpandFromFileTrailingArg(t *testing.T) {
|
||||
f, err := ioutil.TempFile("", "")
|
||||
assert.NoError(t, err)
|
||||
defer os.Remove(f.Name())
|
||||
f.WriteString("hello\nworld\n")
|
||||
f.Close()
|
||||
|
||||
app := New("test", "")
|
||||
arg0 := app.Arg("arg0", "").String()
|
||||
arg1 := app.Arg("arg1", "").String()
|
||||
arg2 := app.Arg("arg2", "").String()
|
||||
|
||||
_, err = app.Parse([]string{"@" + f.Name(), "suffix"})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "hello", *arg0)
|
||||
assert.Equal(t, "world", *arg1)
|
||||
assert.Equal(t, "suffix", *arg2)
|
||||
}
|
||||
|
||||
func TestParserExpandFromFileMultipleSurroundingArgs(t *testing.T) {
|
||||
f, err := ioutil.TempFile("", "")
|
||||
assert.NoError(t, err)
|
||||
defer os.Remove(f.Name())
|
||||
f.WriteString("hello\nworld\n")
|
||||
f.Close()
|
||||
|
||||
app := New("test", "")
|
||||
arg0 := app.Arg("arg0", "").String()
|
||||
arg1 := app.Arg("arg1", "").String()
|
||||
arg2 := app.Arg("arg2", "").String()
|
||||
arg3 := app.Arg("arg3", "").String()
|
||||
|
||||
_, err = app.Parse([]string{"prefix", "@" + f.Name(), "suffix"})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "prefix", *arg0)
|
||||
assert.Equal(t, "hello", *arg1)
|
||||
assert.Equal(t, "world", *arg2)
|
||||
assert.Equal(t, "suffix", *arg3)
|
||||
}
|
||||
|
||||
func TestParserExpandFromFileMultipleFlags(t *testing.T) {
|
||||
f, err := ioutil.TempFile("", "")
|
||||
assert.NoError(t, err)
|
||||
defer os.Remove(f.Name())
|
||||
f.WriteString("--flag1=f1\n--flag2=f2\n")
|
||||
f.Close()
|
||||
|
||||
app := New("test", "")
|
||||
flag0 := app.Flag("flag0", "").String()
|
||||
flag1 := app.Flag("flag1", "").String()
|
||||
flag2 := app.Flag("flag2", "").String()
|
||||
flag3 := app.Flag("flag3", "").String()
|
||||
|
||||
_, err = app.Parse([]string{"--flag0=f0", "@" + f.Name(), "--flag3=f3"})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "f0", *flag0)
|
||||
assert.Equal(t, "f1", *flag1)
|
||||
assert.Equal(t, "f2", *flag2)
|
||||
assert.Equal(t, "f3", *flag3)
|
||||
}
|
||||
|
||||
func TestParseContextPush(t *testing.T) {
|
||||
app := New("test", "")
|
||||
app.Command("foo", "").Command("bar", "")
|
||||
c := tokenize([]string{"foo", "bar"}, false)
|
||||
a := c.Next()
|
||||
assert.Equal(t, TokenArg, a.Type)
|
||||
b := c.Next()
|
||||
assert.Equal(t, TokenArg, b.Type)
|
||||
c.Push(b)
|
||||
c.Push(a)
|
||||
a = c.Next()
|
||||
assert.Equal(t, "foo", a.Value)
|
||||
b = c.Next()
|
||||
assert.Equal(t, "bar", b.Value)
|
||||
}
|
||||
-98
@@ -1,98 +0,0 @@
|
||||
package kingpin
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/url"
|
||||
"os"
|
||||
|
||||
"github.com/alecthomas/assert"
|
||||
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestParseStrings(t *testing.T) {
|
||||
p := parserMixin{}
|
||||
v := p.Strings()
|
||||
p.value.Set("a")
|
||||
p.value.Set("b")
|
||||
assert.Equal(t, []string{"a", "b"}, *v)
|
||||
}
|
||||
|
||||
func TestStringsStringer(t *testing.T) {
|
||||
target := []string{}
|
||||
v := newAccumulator(&target, func(v interface{}) Value { return newStringValue(v.(*string)) })
|
||||
v.Set("hello")
|
||||
v.Set("world")
|
||||
assert.Equal(t, "hello,world", v.String())
|
||||
}
|
||||
|
||||
func TestParseStringMap(t *testing.T) {
|
||||
p := parserMixin{}
|
||||
v := p.StringMap()
|
||||
p.value.Set("a:b")
|
||||
p.value.Set("b:c")
|
||||
assert.Equal(t, map[string]string{"a": "b", "b": "c"}, *v)
|
||||
}
|
||||
|
||||
func TestParseIP(t *testing.T) {
|
||||
p := parserMixin{}
|
||||
v := p.IP()
|
||||
p.value.Set("10.1.1.2")
|
||||
ip := net.ParseIP("10.1.1.2")
|
||||
assert.Equal(t, ip, *v)
|
||||
}
|
||||
|
||||
func TestParseURL(t *testing.T) {
|
||||
p := parserMixin{}
|
||||
v := p.URL()
|
||||
p.value.Set("http://w3.org")
|
||||
u, err := url.Parse("http://w3.org")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, *u, **v)
|
||||
}
|
||||
|
||||
func TestParseExistingFile(t *testing.T) {
|
||||
f, err := ioutil.TempFile("", "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer f.Close()
|
||||
defer os.Remove(f.Name())
|
||||
|
||||
p := parserMixin{}
|
||||
v := p.ExistingFile()
|
||||
err = p.value.Set(f.Name())
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, f.Name(), *v)
|
||||
err = p.value.Set("/etc/hostsDEFINITELYMISSING")
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestParseTCPAddr(t *testing.T) {
|
||||
p := parserMixin{}
|
||||
v := p.TCP()
|
||||
err := p.value.Set("127.0.0.1:1234")
|
||||
assert.NoError(t, err)
|
||||
expected, err := net.ResolveTCPAddr("tcp", "127.0.0.1:1234")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, *expected, **v)
|
||||
}
|
||||
|
||||
func TestParseTCPAddrList(t *testing.T) {
|
||||
p := parserMixin{}
|
||||
_ = p.TCPList()
|
||||
err := p.value.Set("127.0.0.1:1234")
|
||||
assert.NoError(t, err)
|
||||
err = p.value.Set("127.0.0.1:1235")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "127.0.0.1:1234,127.0.0.1:1235", p.value.String())
|
||||
}
|
||||
|
||||
func TestFloat32(t *testing.T) {
|
||||
p := parserMixin{}
|
||||
v := p.Float32()
|
||||
err := p.value.Set("123.45")
|
||||
assert.NoError(t, err)
|
||||
assert.InEpsilon(t, 123.45, *v, 0.001)
|
||||
}
|
||||
-65
@@ -1,65 +0,0 @@
|
||||
package kingpin
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/alecthomas/assert"
|
||||
)
|
||||
|
||||
func TestFormatTwoColumns(t *testing.T) {
|
||||
buf := bytes.NewBuffer(nil)
|
||||
formatTwoColumns(buf, 2, 2, 20, [][2]string{
|
||||
{"--hello", "Hello world help with something that is cool."},
|
||||
})
|
||||
expected := ` --hello Hello
|
||||
world
|
||||
help with
|
||||
something
|
||||
that is
|
||||
cool.
|
||||
`
|
||||
assert.Equal(t, expected, buf.String())
|
||||
}
|
||||
|
||||
func TestFormatTwoColumnsWide(t *testing.T) {
|
||||
samples := [][2]string{
|
||||
{strings.Repeat("x", 29), "29 chars"},
|
||||
{strings.Repeat("x", 30), "30 chars"}}
|
||||
buf := bytes.NewBuffer(nil)
|
||||
formatTwoColumns(buf, 0, 0, 200, samples)
|
||||
expected := `xxxxxxxxxxxxxxxxxxxxxxxxxxxxx29 chars
|
||||
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
30 chars
|
||||
`
|
||||
assert.Equal(t, expected, buf.String())
|
||||
}
|
||||
|
||||
func TestHiddenCommand(t *testing.T) {
|
||||
templates := []struct{ name, template string }{
|
||||
{"default", DefaultUsageTemplate},
|
||||
{"Compact", CompactUsageTemplate},
|
||||
{"Long", LongHelpTemplate},
|
||||
{"Man", ManPageTemplate},
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
t.Log("1")
|
||||
|
||||
a := New("test", "Test").Writer(&buf).Terminate(nil)
|
||||
a.Command("visible", "visible")
|
||||
a.Command("hidden", "hidden").Hidden()
|
||||
|
||||
for _, tp := range templates {
|
||||
buf.Reset()
|
||||
a.UsageTemplate(tp.template)
|
||||
a.Parse(nil)
|
||||
// a.Parse([]string{"--help"})
|
||||
usage := buf.String()
|
||||
t.Logf("Usage for %s is:\n%s\n", tp.name, usage)
|
||||
|
||||
assert.NotContains(t, usage, "hidden")
|
||||
assert.Contains(t, usage, "visible")
|
||||
}
|
||||
}
|
||||
-25
@@ -1,25 +0,0 @@
|
||||
[
|
||||
{"type": "bool", "parser": "strconv.ParseBool(s)"},
|
||||
{"type": "string", "parser": "s, error(nil)", "format": "string(*f.v)", "plural": "Strings"},
|
||||
{"type": "uint", "parser": "strconv.ParseUint(s, 0, 64)", "plural": "Uints"},
|
||||
{"type": "uint8", "parser": "strconv.ParseUint(s, 0, 8)"},
|
||||
{"type": "uint16", "parser": "strconv.ParseUint(s, 0, 16)"},
|
||||
{"type": "uint32", "parser": "strconv.ParseUint(s, 0, 32)"},
|
||||
{"type": "uint64", "parser": "strconv.ParseUint(s, 0, 64)"},
|
||||
{"type": "int", "parser": "strconv.ParseFloat(s, 64)", "plural": "Ints"},
|
||||
{"type": "int8", "parser": "strconv.ParseInt(s, 0, 8)"},
|
||||
{"type": "int16", "parser": "strconv.ParseInt(s, 0, 16)"},
|
||||
{"type": "int32", "parser": "strconv.ParseInt(s, 0, 32)"},
|
||||
{"type": "int64", "parser": "strconv.ParseInt(s, 0, 64)"},
|
||||
{"type": "float64", "parser": "strconv.ParseFloat(s, 64)"},
|
||||
{"type": "float32", "parser": "strconv.ParseFloat(s, 32)"},
|
||||
{"name": "Duration", "type": "time.Duration", "no_value_parser": true},
|
||||
{"name": "IP", "type": "net.IP", "no_value_parser": true},
|
||||
{"name": "TCPAddr", "Type": "*net.TCPAddr", "plural": "TCPList", "no_value_parser": true},
|
||||
{"name": "ExistingFile", "Type": "string", "plural": "ExistingFiles", "no_value_parser": true},
|
||||
{"name": "ExistingDir", "Type": "string", "plural": "ExistingDirs", "no_value_parser": true},
|
||||
{"name": "ExistingFileOrDir", "Type": "string", "plural": "ExistingFilesOrDirs", "no_value_parser": true},
|
||||
{"name": "Regexp", "Type": "*regexp.Regexp", "parser": "regexp.Compile(s)"},
|
||||
{"name": "ResolvedIP", "Type": "net.IP", "parser": "resolveHost(s)", "help": "Resolve a hostname or IP to an IP."},
|
||||
{"name": "HexBytes", "Type": "[]byte", "parser": "hex.DecodeString(s)", "help": "Bytes as a hex string."}
|
||||
]
|
||||
-98
@@ -1,98 +0,0 @@
|
||||
package kingpin
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/alecthomas/assert"
|
||||
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestAccumulatorStrings(t *testing.T) {
|
||||
target := []string{}
|
||||
acc := newAccumulator(&target, func(v interface{}) Value { return newStringValue(v.(*string)) })
|
||||
acc.Set("a")
|
||||
assert.Equal(t, []string{"a"}, target)
|
||||
acc.Set("b")
|
||||
assert.Equal(t, []string{"a", "b"}, target)
|
||||
}
|
||||
|
||||
func TestStrings(t *testing.T) {
|
||||
app := New("", "")
|
||||
app.Arg("a", "").Required().String()
|
||||
app.Arg("b", "").Required().String()
|
||||
c := app.Arg("c", "").Required().Strings()
|
||||
app.Parse([]string{"a", "b", "a", "b"})
|
||||
assert.Equal(t, []string{"a", "b"}, *c)
|
||||
}
|
||||
|
||||
func TestEnum(t *testing.T) {
|
||||
app := New("", "")
|
||||
a := app.Arg("a", "").Enum("one", "two", "three")
|
||||
_, err := app.Parse([]string{"moo"})
|
||||
assert.Error(t, err)
|
||||
_, err = app.Parse([]string{"one"})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "one", *a)
|
||||
}
|
||||
|
||||
func TestEnumVar(t *testing.T) {
|
||||
app := New("", "")
|
||||
var a string
|
||||
app.Arg("a", "").EnumVar(&a, "one", "two", "three")
|
||||
_, err := app.Parse([]string{"moo"})
|
||||
assert.Error(t, err)
|
||||
_, err = app.Parse([]string{"one"})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "one", a)
|
||||
}
|
||||
|
||||
func TestCounter(t *testing.T) {
|
||||
app := New("", "")
|
||||
c := app.Flag("f", "").Counter()
|
||||
_, err := app.Parse([]string{"--f", "--f", "--f"})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 3, *c)
|
||||
}
|
||||
|
||||
func TestIPv4Addr(t *testing.T) {
|
||||
app := newTestApp()
|
||||
flag := app.Flag("addr", "").ResolvedIP()
|
||||
_, err := app.Parse([]string{"--addr", net.IPv4(1, 2, 3, 4).String()})
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, *flag)
|
||||
assert.Equal(t, net.IPv4(1, 2, 3, 4), *flag)
|
||||
}
|
||||
|
||||
func TestInvalidIPv4Addr(t *testing.T) {
|
||||
app := newTestApp()
|
||||
app.Flag("addr", "").ResolvedIP()
|
||||
_, err := app.Parse([]string{"--addr", "1.2.3.256"})
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestIPv6Addr(t *testing.T) {
|
||||
app := newTestApp()
|
||||
flag := app.Flag("addr", "").ResolvedIP()
|
||||
_, err := app.Parse([]string{"--addr", net.IPv6interfacelocalallnodes.String()})
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, *flag)
|
||||
assert.Equal(t, net.IPv6interfacelocalallnodes, *flag)
|
||||
}
|
||||
|
||||
func TestHexBytes(t *testing.T) {
|
||||
app := newTestApp()
|
||||
actual := app.Arg("bytes", "").HexBytes()
|
||||
_, err := app.Parse([]string{"01020aff"})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, []byte{0x01, 0x02, 0x0a, 0xff}, *actual)
|
||||
}
|
||||
|
||||
func TestSetValueDoesNotReset(t *testing.T) {
|
||||
app := newTestApp()
|
||||
mapping := map[string]string{
|
||||
"key": "value",
|
||||
}
|
||||
app.Flag("set", "").StringMapVar(&mapping)
|
||||
assert.NotEmpty(t, mapping)
|
||||
}
|
||||
Reference in New Issue
Block a user