mirror of https://github.com/status-im/fathom.git
update vendored deps
This commit is contained in:
parent
9d0bee4372
commit
6161442487
|
@ -0,0 +1,19 @@
|
||||||
|
Copyright (C) 2014 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.
|
|
@ -0,0 +1,671 @@
|
||||||
|
# Kingpin - A Go (golang) command line and flag parser [![](https://godoc.org/github.com/alecthomas/kingpin?status.svg)](http://godoc.org/github.com/alecthomas/kingpin) [![Build Status](https://travis-ci.org/alecthomas/kingpin.svg?branch=master)](https://travis-ci.org/alecthomas/kingpin)
|
||||||
|
|
||||||
|
<!-- 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", *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()`.
|
||||||
|
|
||||||
|
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 = new(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(args []string) []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>
|
||||||
|
```
|
|
@ -0,0 +1,42 @@
|
||||||
|
package kingpin
|
||||||
|
|
||||||
|
// Action callback executed at various stages after all values are populated.
|
||||||
|
// The application, commands, arguments and flags all have corresponding
|
||||||
|
// actions.
|
||||||
|
type Action func(*ParseContext) error
|
||||||
|
|
||||||
|
type actionMixin struct {
|
||||||
|
actions []Action
|
||||||
|
preActions []Action
|
||||||
|
}
|
||||||
|
|
||||||
|
type actionApplier interface {
|
||||||
|
applyActions(*ParseContext) error
|
||||||
|
applyPreActions(*ParseContext) error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *actionMixin) addAction(action Action) {
|
||||||
|
a.actions = append(a.actions, action)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *actionMixin) addPreAction(action Action) {
|
||||||
|
a.preActions = append(a.preActions, action)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *actionMixin) applyActions(context *ParseContext) error {
|
||||||
|
for _, action := range a.actions {
|
||||||
|
if err := action(context); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *actionMixin) applyPreActions(context *ParseContext) error {
|
||||||
|
for _, preAction := range a.preActions {
|
||||||
|
if err := preAction(context); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,685 @@
|
||||||
|
package kingpin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrCommandNotSpecified = fmt.Errorf("command not specified")
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
envarTransformRegexp = regexp.MustCompile(`[^a-zA-Z0-9_]+`)
|
||||||
|
)
|
||||||
|
|
||||||
|
type ApplicationValidator func(*Application) error
|
||||||
|
|
||||||
|
// An Application contains the definitions of flags, arguments and commands
|
||||||
|
// for an application.
|
||||||
|
type Application struct {
|
||||||
|
cmdMixin
|
||||||
|
initialized bool
|
||||||
|
|
||||||
|
Name string
|
||||||
|
Help string
|
||||||
|
|
||||||
|
author string
|
||||||
|
version string
|
||||||
|
errorWriter io.Writer // Destination for errors.
|
||||||
|
usageWriter io.Writer // Destination for usage
|
||||||
|
usageTemplate string
|
||||||
|
validator ApplicationValidator
|
||||||
|
terminate func(status int) // See Terminate()
|
||||||
|
noInterspersed bool // can flags be interspersed with args (or must they come first)
|
||||||
|
defaultEnvars bool
|
||||||
|
completion bool
|
||||||
|
|
||||||
|
// Help flag. Exposed for user customisation.
|
||||||
|
HelpFlag *FlagClause
|
||||||
|
// Help command. Exposed for user customisation. May be nil.
|
||||||
|
HelpCommand *CmdClause
|
||||||
|
// Version flag. Exposed for user customisation. May be nil.
|
||||||
|
VersionFlag *FlagClause
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates a new Kingpin application instance.
|
||||||
|
func New(name, help string) *Application {
|
||||||
|
a := &Application{
|
||||||
|
Name: name,
|
||||||
|
Help: help,
|
||||||
|
errorWriter: os.Stderr, // Left for backwards compatibility purposes.
|
||||||
|
usageWriter: os.Stderr,
|
||||||
|
usageTemplate: DefaultUsageTemplate,
|
||||||
|
terminate: os.Exit,
|
||||||
|
}
|
||||||
|
a.flagGroup = newFlagGroup()
|
||||||
|
a.argGroup = newArgGroup()
|
||||||
|
a.cmdGroup = newCmdGroup(a)
|
||||||
|
a.HelpFlag = a.Flag("help", "Show context-sensitive help (also try --help-long and --help-man).")
|
||||||
|
a.HelpFlag.Bool()
|
||||||
|
a.Flag("help-long", "Generate long help.").Hidden().PreAction(a.generateLongHelp).Bool()
|
||||||
|
a.Flag("help-man", "Generate a man page.").Hidden().PreAction(a.generateManPage).Bool()
|
||||||
|
a.Flag("completion-bash", "Output possible completions for the given args.").Hidden().BoolVar(&a.completion)
|
||||||
|
a.Flag("completion-script-bash", "Generate completion script for bash.").Hidden().PreAction(a.generateBashCompletionScript).Bool()
|
||||||
|
a.Flag("completion-script-zsh", "Generate completion script for ZSH.").Hidden().PreAction(a.generateZSHCompletionScript).Bool()
|
||||||
|
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Application) generateLongHelp(c *ParseContext) error {
|
||||||
|
a.Writer(os.Stdout)
|
||||||
|
if err := a.UsageForContextWithTemplate(c, 2, LongHelpTemplate); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
a.terminate(0)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Application) generateManPage(c *ParseContext) error {
|
||||||
|
a.Writer(os.Stdout)
|
||||||
|
if err := a.UsageForContextWithTemplate(c, 2, ManPageTemplate); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
a.terminate(0)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Application) generateBashCompletionScript(c *ParseContext) error {
|
||||||
|
a.Writer(os.Stdout)
|
||||||
|
if err := a.UsageForContextWithTemplate(c, 2, BashCompletionTemplate); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
a.terminate(0)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Application) generateZSHCompletionScript(c *ParseContext) error {
|
||||||
|
a.Writer(os.Stdout)
|
||||||
|
if err := a.UsageForContextWithTemplate(c, 2, ZshCompletionTemplate); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
a.terminate(0)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultEnvars configures all flags (that do not already have an associated
|
||||||
|
// envar) to use a default environment variable in the form "<app>_<flag>".
|
||||||
|
//
|
||||||
|
// For example, if the application is named "foo" and a flag is named "bar-
|
||||||
|
// waz" the environment variable: "FOO_BAR_WAZ".
|
||||||
|
func (a *Application) DefaultEnvars() *Application {
|
||||||
|
a.defaultEnvars = true
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
// Terminate specifies the termination handler. Defaults to os.Exit(status).
|
||||||
|
// If nil is passed, a no-op function will be used.
|
||||||
|
func (a *Application) Terminate(terminate func(int)) *Application {
|
||||||
|
if terminate == nil {
|
||||||
|
terminate = func(int) {}
|
||||||
|
}
|
||||||
|
a.terminate = terminate
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
// Writer specifies the writer to use for usage and errors. Defaults to os.Stderr.
|
||||||
|
// DEPRECATED: See ErrorWriter and UsageWriter.
|
||||||
|
func (a *Application) Writer(w io.Writer) *Application {
|
||||||
|
a.errorWriter = w
|
||||||
|
a.usageWriter = w
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrorWriter sets the io.Writer to use for errors.
|
||||||
|
func (a *Application) ErrorWriter(w io.Writer) *Application {
|
||||||
|
a.usageWriter = w
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
// UsageWriter sets the io.Writer to use for errors.
|
||||||
|
func (a *Application) UsageWriter(w io.Writer) *Application {
|
||||||
|
a.usageWriter = w
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
// UsageTemplate specifies the text template to use when displaying usage
|
||||||
|
// information. The default is UsageTemplate.
|
||||||
|
func (a *Application) UsageTemplate(template string) *Application {
|
||||||
|
a.usageTemplate = template
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate sets a validation function to run when parsing.
|
||||||
|
func (a *Application) Validate(validator ApplicationValidator) *Application {
|
||||||
|
a.validator = validator
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseContext parses the given command line and returns the fully populated
|
||||||
|
// ParseContext.
|
||||||
|
func (a *Application) ParseContext(args []string) (*ParseContext, error) {
|
||||||
|
return a.parseContext(false, args)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Application) parseContext(ignoreDefault bool, args []string) (*ParseContext, error) {
|
||||||
|
if err := a.init(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
context := tokenize(args, ignoreDefault)
|
||||||
|
err := parse(context, a)
|
||||||
|
return context, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse parses command-line arguments. It returns the selected command and an
|
||||||
|
// error. The selected command will be a space separated subcommand, if
|
||||||
|
// subcommands have been configured.
|
||||||
|
//
|
||||||
|
// This will populate all flag and argument values, call all callbacks, and so
|
||||||
|
// on.
|
||||||
|
func (a *Application) Parse(args []string) (command string, err error) {
|
||||||
|
|
||||||
|
context, parseErr := a.ParseContext(args)
|
||||||
|
selected := []string{}
|
||||||
|
var setValuesErr error
|
||||||
|
|
||||||
|
if context == nil {
|
||||||
|
// Since we do not throw error immediately, there could be a case
|
||||||
|
// where a context returns nil. Protect against that.
|
||||||
|
return "", parseErr
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = a.setDefaults(context); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
selected, setValuesErr = a.setValues(context)
|
||||||
|
|
||||||
|
if err = a.applyPreActions(context, !a.completion); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if a.completion {
|
||||||
|
a.generateBashCompletion(context)
|
||||||
|
a.terminate(0)
|
||||||
|
} else {
|
||||||
|
if parseErr != nil {
|
||||||
|
return "", parseErr
|
||||||
|
}
|
||||||
|
|
||||||
|
a.maybeHelp(context)
|
||||||
|
if !context.EOL() {
|
||||||
|
return "", fmt.Errorf("unexpected argument '%s'", context.Peek())
|
||||||
|
}
|
||||||
|
|
||||||
|
if setValuesErr != nil {
|
||||||
|
return "", setValuesErr
|
||||||
|
}
|
||||||
|
|
||||||
|
command, err = a.execute(context, selected)
|
||||||
|
if err == ErrCommandNotSpecified {
|
||||||
|
a.writeUsage(context, nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return command, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Application) writeUsage(context *ParseContext, err error) {
|
||||||
|
if err != nil {
|
||||||
|
a.Errorf("%s", err)
|
||||||
|
}
|
||||||
|
if err := a.UsageForContext(context); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
a.terminate(1)
|
||||||
|
} else {
|
||||||
|
a.terminate(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Application) maybeHelp(context *ParseContext) {
|
||||||
|
for _, element := range context.Elements {
|
||||||
|
if flag, ok := element.Clause.(*FlagClause); ok && flag == a.HelpFlag {
|
||||||
|
// Re-parse the command-line ignoring defaults, so that help works correctly.
|
||||||
|
context, _ = a.parseContext(true, context.rawArgs)
|
||||||
|
a.writeUsage(context, nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Version adds a --version flag for displaying the application version.
|
||||||
|
func (a *Application) Version(version string) *Application {
|
||||||
|
a.version = version
|
||||||
|
a.VersionFlag = a.Flag("version", "Show application version.").PreAction(func(*ParseContext) error {
|
||||||
|
fmt.Fprintln(a.usageWriter, version)
|
||||||
|
a.terminate(0)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
a.VersionFlag.Bool()
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
// Author sets the author output by some help templates.
|
||||||
|
func (a *Application) Author(author string) *Application {
|
||||||
|
a.author = author
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
// Action callback to call when all values are populated and parsing is
|
||||||
|
// complete, but before any command, flag or argument actions.
|
||||||
|
//
|
||||||
|
// All Action() callbacks are called in the order they are encountered on the
|
||||||
|
// command line.
|
||||||
|
func (a *Application) Action(action Action) *Application {
|
||||||
|
a.addAction(action)
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
// Action called after parsing completes but before validation and execution.
|
||||||
|
func (a *Application) PreAction(action Action) *Application {
|
||||||
|
a.addPreAction(action)
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command adds a new top-level command.
|
||||||
|
func (a *Application) Command(name, help string) *CmdClause {
|
||||||
|
return a.addCommand(name, help)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Interspersed control if flags can be interspersed with positional arguments
|
||||||
|
//
|
||||||
|
// true (the default) means that they can, false means that all the flags must appear before the first positional arguments.
|
||||||
|
func (a *Application) Interspersed(interspersed bool) *Application {
|
||||||
|
a.noInterspersed = !interspersed
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Application) defaultEnvarPrefix() string {
|
||||||
|
if a.defaultEnvars {
|
||||||
|
return a.Name
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Application) init() error {
|
||||||
|
if a.initialized {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if a.cmdGroup.have() && a.argGroup.have() {
|
||||||
|
return fmt.Errorf("can't mix top-level Arg()s with Command()s")
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we have subcommands, add a help command at the top-level.
|
||||||
|
if a.cmdGroup.have() {
|
||||||
|
var command []string
|
||||||
|
a.HelpCommand = a.Command("help", "Show help.").PreAction(func(context *ParseContext) error {
|
||||||
|
a.Usage(command)
|
||||||
|
a.terminate(0)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
a.HelpCommand.Arg("command", "Show help on command.").StringsVar(&command)
|
||||||
|
// Make help first command.
|
||||||
|
l := len(a.commandOrder)
|
||||||
|
a.commandOrder = append(a.commandOrder[l-1:l], a.commandOrder[:l-1]...)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := a.flagGroup.init(a.defaultEnvarPrefix()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := a.cmdGroup.init(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := a.argGroup.init(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, cmd := range a.commands {
|
||||||
|
if err := cmd.init(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
flagGroups := []*flagGroup{a.flagGroup}
|
||||||
|
for _, cmd := range a.commandOrder {
|
||||||
|
if err := checkDuplicateFlags(cmd, flagGroups); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
a.initialized = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recursively check commands for duplicate flags.
|
||||||
|
func checkDuplicateFlags(current *CmdClause, flagGroups []*flagGroup) error {
|
||||||
|
// Check for duplicates.
|
||||||
|
for _, flags := range flagGroups {
|
||||||
|
for _, flag := range current.flagOrder {
|
||||||
|
if flag.shorthand != 0 {
|
||||||
|
if _, ok := flags.short[string(flag.shorthand)]; ok {
|
||||||
|
return fmt.Errorf("duplicate short flag -%c", flag.shorthand)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if _, ok := flags.long[flag.name]; ok {
|
||||||
|
return fmt.Errorf("duplicate long flag --%s", flag.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
flagGroups = append(flagGroups, current.flagGroup)
|
||||||
|
// Check subcommands.
|
||||||
|
for _, subcmd := range current.commandOrder {
|
||||||
|
if err := checkDuplicateFlags(subcmd, flagGroups); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Application) execute(context *ParseContext, selected []string) (string, error) {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if err = a.validateRequired(context); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = a.applyValidators(context); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = a.applyActions(context); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
command := strings.Join(selected, " ")
|
||||||
|
if command == "" && a.cmdGroup.have() {
|
||||||
|
return "", ErrCommandNotSpecified
|
||||||
|
}
|
||||||
|
return command, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Application) setDefaults(context *ParseContext) error {
|
||||||
|
flagElements := map[string]*ParseElement{}
|
||||||
|
for _, element := range context.Elements {
|
||||||
|
if flag, ok := element.Clause.(*FlagClause); ok {
|
||||||
|
flagElements[flag.name] = element
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
argElements := map[string]*ParseElement{}
|
||||||
|
for _, element := range context.Elements {
|
||||||
|
if arg, ok := element.Clause.(*ArgClause); ok {
|
||||||
|
argElements[arg.name] = element
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check required flags and set defaults.
|
||||||
|
for _, flag := range context.flags.long {
|
||||||
|
if flagElements[flag.name] == nil {
|
||||||
|
if err := flag.setDefault(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, arg := range context.arguments.args {
|
||||||
|
if argElements[arg.name] == nil {
|
||||||
|
if err := arg.setDefault(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Application) validateRequired(context *ParseContext) error {
|
||||||
|
flagElements := map[string]*ParseElement{}
|
||||||
|
for _, element := range context.Elements {
|
||||||
|
if flag, ok := element.Clause.(*FlagClause); ok {
|
||||||
|
flagElements[flag.name] = element
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
argElements := map[string]*ParseElement{}
|
||||||
|
for _, element := range context.Elements {
|
||||||
|
if arg, ok := element.Clause.(*ArgClause); ok {
|
||||||
|
argElements[arg.name] = element
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check required flags and set defaults.
|
||||||
|
for _, flag := range context.flags.long {
|
||||||
|
if flagElements[flag.name] == nil {
|
||||||
|
// Check required flags were provided.
|
||||||
|
if flag.needsValue() {
|
||||||
|
return fmt.Errorf("required flag --%s not provided", flag.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, arg := range context.arguments.args {
|
||||||
|
if argElements[arg.name] == nil {
|
||||||
|
if arg.needsValue() {
|
||||||
|
return fmt.Errorf("required argument '%s' not provided", arg.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Application) setValues(context *ParseContext) (selected []string, err error) {
|
||||||
|
// Set all arg and flag values.
|
||||||
|
var (
|
||||||
|
lastCmd *CmdClause
|
||||||
|
flagSet = map[string]struct{}{}
|
||||||
|
)
|
||||||
|
for _, element := range context.Elements {
|
||||||
|
switch clause := element.Clause.(type) {
|
||||||
|
case *FlagClause:
|
||||||
|
if _, ok := flagSet[clause.name]; ok {
|
||||||
|
if v, ok := clause.value.(repeatableFlag); !ok || !v.IsCumulative() {
|
||||||
|
return nil, fmt.Errorf("flag '%s' cannot be repeated", clause.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err = clause.value.Set(*element.Value); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
flagSet[clause.name] = struct{}{}
|
||||||
|
|
||||||
|
case *ArgClause:
|
||||||
|
if err = clause.value.Set(*element.Value); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
case *CmdClause:
|
||||||
|
if clause.validator != nil {
|
||||||
|
if err = clause.validator(clause); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
selected = append(selected, clause.name)
|
||||||
|
lastCmd = clause
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if lastCmd != nil && len(lastCmd.commands) > 0 {
|
||||||
|
return nil, fmt.Errorf("must select a subcommand of '%s'", lastCmd.FullCommand())
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Application) applyValidators(context *ParseContext) (err error) {
|
||||||
|
// Call command validation functions.
|
||||||
|
for _, element := range context.Elements {
|
||||||
|
if cmd, ok := element.Clause.(*CmdClause); ok && cmd.validator != nil {
|
||||||
|
if err = cmd.validator(cmd); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if a.validator != nil {
|
||||||
|
err = a.validator(a)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Application) applyPreActions(context *ParseContext, dispatch bool) error {
|
||||||
|
if err := a.actionMixin.applyPreActions(context); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Dispatch to actions.
|
||||||
|
if dispatch {
|
||||||
|
for _, element := range context.Elements {
|
||||||
|
if applier, ok := element.Clause.(actionApplier); ok {
|
||||||
|
if err := applier.applyPreActions(context); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Application) applyActions(context *ParseContext) error {
|
||||||
|
if err := a.actionMixin.applyActions(context); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Dispatch to actions.
|
||||||
|
for _, element := range context.Elements {
|
||||||
|
if applier, ok := element.Clause.(actionApplier); ok {
|
||||||
|
if err := applier.applyActions(context); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Errorf prints an error message to w in the format "<appname>: error: <message>".
|
||||||
|
func (a *Application) Errorf(format string, args ...interface{}) {
|
||||||
|
fmt.Fprintf(a.errorWriter, a.Name+": error: "+format+"\n", args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fatalf writes a formatted error to w then terminates with exit status 1.
|
||||||
|
func (a *Application) Fatalf(format string, args ...interface{}) {
|
||||||
|
a.Errorf(format, args...)
|
||||||
|
a.terminate(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FatalUsage prints an error message followed by usage information, then
|
||||||
|
// exits with a non-zero status.
|
||||||
|
func (a *Application) FatalUsage(format string, args ...interface{}) {
|
||||||
|
a.Errorf(format, args...)
|
||||||
|
// Force usage to go to error output.
|
||||||
|
a.usageWriter = a.errorWriter
|
||||||
|
a.Usage([]string{})
|
||||||
|
a.terminate(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FatalUsageContext writes a printf formatted error message to w, then usage
|
||||||
|
// information for the given ParseContext, before exiting.
|
||||||
|
func (a *Application) FatalUsageContext(context *ParseContext, format string, args ...interface{}) {
|
||||||
|
a.Errorf(format, args...)
|
||||||
|
if err := a.UsageForContext(context); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
a.terminate(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FatalIfError prints an error and exits if err is not nil. The error is printed
|
||||||
|
// with the given formatted string, if any.
|
||||||
|
func (a *Application) FatalIfError(err error, format string, args ...interface{}) {
|
||||||
|
if err != nil {
|
||||||
|
prefix := ""
|
||||||
|
if format != "" {
|
||||||
|
prefix = fmt.Sprintf(format, args...) + ": "
|
||||||
|
}
|
||||||
|
a.Errorf(prefix+"%s", err)
|
||||||
|
a.terminate(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Application) completionOptions(context *ParseContext) []string {
|
||||||
|
args := context.rawArgs
|
||||||
|
|
||||||
|
var (
|
||||||
|
currArg string
|
||||||
|
prevArg string
|
||||||
|
target cmdMixin
|
||||||
|
)
|
||||||
|
|
||||||
|
numArgs := len(args)
|
||||||
|
if numArgs > 1 {
|
||||||
|
args = args[1:]
|
||||||
|
currArg = args[len(args)-1]
|
||||||
|
}
|
||||||
|
if numArgs > 2 {
|
||||||
|
prevArg = args[len(args)-2]
|
||||||
|
}
|
||||||
|
|
||||||
|
target = a.cmdMixin
|
||||||
|
if context.SelectedCommand != nil {
|
||||||
|
// A subcommand was in use. We will use it as the target
|
||||||
|
target = context.SelectedCommand.cmdMixin
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currArg != "" && strings.HasPrefix(currArg, "--")) || strings.HasPrefix(prevArg, "--") {
|
||||||
|
// Perform completion for A flag. The last/current argument started with "-"
|
||||||
|
var (
|
||||||
|
flagName string // The name of a flag if given (could be half complete)
|
||||||
|
flagValue string // The value assigned to a flag (if given) (could be half complete)
|
||||||
|
)
|
||||||
|
|
||||||
|
if strings.HasPrefix(prevArg, "--") && !strings.HasPrefix(currArg, "--") {
|
||||||
|
// Matches: ./myApp --flag value
|
||||||
|
// Wont Match: ./myApp --flag --
|
||||||
|
flagName = prevArg[2:] // Strip the "--"
|
||||||
|
flagValue = currArg
|
||||||
|
} else if strings.HasPrefix(currArg, "--") {
|
||||||
|
// Matches: ./myApp --flag --
|
||||||
|
// Matches: ./myApp --flag somevalue --
|
||||||
|
// Matches: ./myApp --
|
||||||
|
flagName = currArg[2:] // Strip the "--"
|
||||||
|
}
|
||||||
|
|
||||||
|
options, flagMatched, valueMatched := target.FlagCompletion(flagName, flagValue)
|
||||||
|
if valueMatched {
|
||||||
|
// Value Matched. Show cmdCompletions
|
||||||
|
return target.CmdCompletion(context)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add top level flags if we're not at the top level and no match was found.
|
||||||
|
if context.SelectedCommand != nil && !flagMatched {
|
||||||
|
topOptions, topFlagMatched, topValueMatched := a.FlagCompletion(flagName, flagValue)
|
||||||
|
if topValueMatched {
|
||||||
|
// Value Matched. Back to cmdCompletions
|
||||||
|
return target.CmdCompletion(context)
|
||||||
|
}
|
||||||
|
|
||||||
|
if topFlagMatched {
|
||||||
|
// Top level had a flag which matched the input. Return it's options.
|
||||||
|
options = topOptions
|
||||||
|
} else {
|
||||||
|
// Add top level flags
|
||||||
|
options = append(options, topOptions...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return options
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perform completion for sub commands and arguments.
|
||||||
|
return target.CmdCompletion(context)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Application) generateBashCompletion(context *ParseContext) {
|
||||||
|
options := a.completionOptions(context)
|
||||||
|
fmt.Printf("%s", strings.Join(options, "\n"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func envarTransform(name string) string {
|
||||||
|
return strings.ToUpper(envarTransformRegexp.ReplaceAllString(name, "_"))
|
||||||
|
}
|
|
@ -0,0 +1,185 @@
|
||||||
|
package kingpin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
type argGroup struct {
|
||||||
|
args []*ArgClause
|
||||||
|
}
|
||||||
|
|
||||||
|
func newArgGroup() *argGroup {
|
||||||
|
return &argGroup{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *argGroup) have() bool {
|
||||||
|
return len(a.args) > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetArg gets an argument definition.
|
||||||
|
//
|
||||||
|
// This allows existing arguments to be modified after definition but before parsing. Useful for
|
||||||
|
// modular applications.
|
||||||
|
func (a *argGroup) GetArg(name string) *ArgClause {
|
||||||
|
for _, arg := range a.args {
|
||||||
|
if arg.name == name {
|
||||||
|
return arg
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *argGroup) Arg(name, help string) *ArgClause {
|
||||||
|
arg := newArg(name, help)
|
||||||
|
a.args = append(a.args, arg)
|
||||||
|
return arg
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *argGroup) init() error {
|
||||||
|
required := 0
|
||||||
|
seen := map[string]struct{}{}
|
||||||
|
previousArgMustBeLast := false
|
||||||
|
for i, arg := range a.args {
|
||||||
|
if previousArgMustBeLast {
|
||||||
|
return fmt.Errorf("Args() can't be followed by another argument '%s'", arg.name)
|
||||||
|
}
|
||||||
|
if arg.consumesRemainder() {
|
||||||
|
previousArgMustBeLast = true
|
||||||
|
}
|
||||||
|
if _, ok := seen[arg.name]; ok {
|
||||||
|
return fmt.Errorf("duplicate argument '%s'", arg.name)
|
||||||
|
}
|
||||||
|
seen[arg.name] = struct{}{}
|
||||||
|
if arg.required && required != i {
|
||||||
|
return fmt.Errorf("required arguments found after non-required")
|
||||||
|
}
|
||||||
|
if arg.required {
|
||||||
|
required++
|
||||||
|
}
|
||||||
|
if err := arg.init(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type ArgClause struct {
|
||||||
|
actionMixin
|
||||||
|
parserMixin
|
||||||
|
completionsMixin
|
||||||
|
envarMixin
|
||||||
|
name string
|
||||||
|
help string
|
||||||
|
defaultValues []string
|
||||||
|
required bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func newArg(name, help string) *ArgClause {
|
||||||
|
a := &ArgClause{
|
||||||
|
name: name,
|
||||||
|
help: help,
|
||||||
|
}
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ArgClause) setDefault() error {
|
||||||
|
if a.HasEnvarValue() {
|
||||||
|
if v, ok := a.value.(remainderArg); !ok || !v.IsCumulative() {
|
||||||
|
// Use the value as-is
|
||||||
|
return a.value.Set(a.GetEnvarValue())
|
||||||
|
} else {
|
||||||
|
for _, value := range a.GetSplitEnvarValue() {
|
||||||
|
if err := a.value.Set(value); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(a.defaultValues) > 0 {
|
||||||
|
for _, defaultValue := range a.defaultValues {
|
||||||
|
if err := a.value.Set(defaultValue); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ArgClause) needsValue() bool {
|
||||||
|
haveDefault := len(a.defaultValues) > 0
|
||||||
|
return a.required && !(haveDefault || a.HasEnvarValue())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ArgClause) consumesRemainder() bool {
|
||||||
|
if r, ok := a.value.(remainderArg); ok {
|
||||||
|
return r.IsCumulative()
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Required arguments must be input by the user. They can not have a Default() value provided.
|
||||||
|
func (a *ArgClause) Required() *ArgClause {
|
||||||
|
a.required = true
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default values for this argument. They *must* be parseable by the value of the argument.
|
||||||
|
func (a *ArgClause) Default(values ...string) *ArgClause {
|
||||||
|
a.defaultValues = values
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
// Envar overrides the default value(s) for a flag from an environment variable,
|
||||||
|
// if it is set. Several default values can be provided by using new lines to
|
||||||
|
// separate them.
|
||||||
|
func (a *ArgClause) Envar(name string) *ArgClause {
|
||||||
|
a.envar = name
|
||||||
|
a.noEnvar = false
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
// NoEnvar forces environment variable defaults to be disabled for this flag.
|
||||||
|
// Most useful in conjunction with app.DefaultEnvars().
|
||||||
|
func (a *ArgClause) NoEnvar() *ArgClause {
|
||||||
|
a.envar = ""
|
||||||
|
a.noEnvar = true
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ArgClause) Action(action Action) *ArgClause {
|
||||||
|
a.addAction(action)
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ArgClause) PreAction(action Action) *ArgClause {
|
||||||
|
a.addPreAction(action)
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
// HintAction registers a HintAction (function) for the arg to provide completions
|
||||||
|
func (a *ArgClause) HintAction(action HintAction) *ArgClause {
|
||||||
|
a.addHintAction(action)
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
// HintOptions registers any number of options for the flag to provide completions
|
||||||
|
func (a *ArgClause) HintOptions(options ...string) *ArgClause {
|
||||||
|
a.addHintAction(func() []string {
|
||||||
|
return options
|
||||||
|
})
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ArgClause) init() error {
|
||||||
|
if a.required && len(a.defaultValues) > 0 {
|
||||||
|
return fmt.Errorf("required argument '%s' with unusable default value", a.name)
|
||||||
|
}
|
||||||
|
if a.value == nil {
|
||||||
|
return fmt.Errorf("no parser defined for arg '%s'", a.name)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,274 @@
|
||||||
|
package kingpin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type cmdMixin struct {
|
||||||
|
*flagGroup
|
||||||
|
*argGroup
|
||||||
|
*cmdGroup
|
||||||
|
actionMixin
|
||||||
|
}
|
||||||
|
|
||||||
|
// CmdCompletion returns completion options for arguments, if that's where
|
||||||
|
// parsing left off, or commands if there aren't any unsatisfied args.
|
||||||
|
func (c *cmdMixin) CmdCompletion(context *ParseContext) []string {
|
||||||
|
var options []string
|
||||||
|
|
||||||
|
// Count args already satisfied - we won't complete those, and add any
|
||||||
|
// default commands' alternatives, since they weren't listed explicitly
|
||||||
|
// and the user may want to explicitly list something else.
|
||||||
|
argsSatisfied := 0
|
||||||
|
for _, el := range context.Elements {
|
||||||
|
switch clause := el.Clause.(type) {
|
||||||
|
case *ArgClause:
|
||||||
|
if el.Value != nil && *el.Value != "" {
|
||||||
|
argsSatisfied++
|
||||||
|
}
|
||||||
|
case *CmdClause:
|
||||||
|
options = append(options, clause.completionAlts...)
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if argsSatisfied < len(c.argGroup.args) {
|
||||||
|
// Since not all args have been satisfied, show options for the current one
|
||||||
|
options = append(options, c.argGroup.args[argsSatisfied].resolveCompletions()...)
|
||||||
|
} else {
|
||||||
|
// If all args are satisfied, then go back to completing commands
|
||||||
|
for _, cmd := range c.cmdGroup.commandOrder {
|
||||||
|
if !cmd.hidden {
|
||||||
|
options = append(options, cmd.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return options
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cmdMixin) FlagCompletion(flagName string, flagValue string) (choices []string, flagMatch bool, optionMatch bool) {
|
||||||
|
// Check if flagName matches a known flag.
|
||||||
|
// If it does, show the options for the flag
|
||||||
|
// Otherwise, show all flags
|
||||||
|
|
||||||
|
options := []string{}
|
||||||
|
|
||||||
|
for _, flag := range c.flagGroup.flagOrder {
|
||||||
|
// Loop through each flag and determine if a match exists
|
||||||
|
if flag.name == flagName {
|
||||||
|
// User typed entire flag. Need to look for flag options.
|
||||||
|
options = flag.resolveCompletions()
|
||||||
|
if len(options) == 0 {
|
||||||
|
// No Options to Choose From, Assume Match.
|
||||||
|
return options, true, true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loop options to find if the user specified value matches
|
||||||
|
isPrefix := false
|
||||||
|
matched := false
|
||||||
|
|
||||||
|
for _, opt := range options {
|
||||||
|
if flagValue == opt {
|
||||||
|
matched = true
|
||||||
|
} else if strings.HasPrefix(opt, flagValue) {
|
||||||
|
isPrefix = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Matched Flag Directly
|
||||||
|
// Flag Value Not Prefixed, and Matched Directly
|
||||||
|
return options, true, !isPrefix && matched
|
||||||
|
}
|
||||||
|
|
||||||
|
if !flag.hidden {
|
||||||
|
options = append(options, "--"+flag.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// No Flag directly matched.
|
||||||
|
return options, false, false
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
type cmdGroup struct {
|
||||||
|
app *Application
|
||||||
|
parent *CmdClause
|
||||||
|
commands map[string]*CmdClause
|
||||||
|
commandOrder []*CmdClause
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cmdGroup) defaultSubcommand() *CmdClause {
|
||||||
|
for _, cmd := range c.commandOrder {
|
||||||
|
if cmd.isDefault {
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cmdGroup) cmdNames() []string {
|
||||||
|
names := make([]string, 0, len(c.commandOrder))
|
||||||
|
for _, cmd := range c.commandOrder {
|
||||||
|
names = append(names, cmd.name)
|
||||||
|
}
|
||||||
|
return names
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetArg gets a command definition.
|
||||||
|
//
|
||||||
|
// This allows existing commands to be modified after definition but before parsing. Useful for
|
||||||
|
// modular applications.
|
||||||
|
func (c *cmdGroup) GetCommand(name string) *CmdClause {
|
||||||
|
return c.commands[name]
|
||||||
|
}
|
||||||
|
|
||||||
|
func newCmdGroup(app *Application) *cmdGroup {
|
||||||
|
return &cmdGroup{
|
||||||
|
app: app,
|
||||||
|
commands: make(map[string]*CmdClause),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cmdGroup) flattenedCommands() (out []*CmdClause) {
|
||||||
|
for _, cmd := range c.commandOrder {
|
||||||
|
if len(cmd.commands) == 0 {
|
||||||
|
out = append(out, cmd)
|
||||||
|
}
|
||||||
|
out = append(out, cmd.flattenedCommands()...)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cmdGroup) addCommand(name, help string) *CmdClause {
|
||||||
|
cmd := newCommand(c.app, name, help)
|
||||||
|
c.commands[name] = cmd
|
||||||
|
c.commandOrder = append(c.commandOrder, cmd)
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cmdGroup) init() error {
|
||||||
|
seen := map[string]bool{}
|
||||||
|
if c.defaultSubcommand() != nil && !c.have() {
|
||||||
|
return fmt.Errorf("default subcommand %q provided but no subcommands defined", c.defaultSubcommand().name)
|
||||||
|
}
|
||||||
|
defaults := []string{}
|
||||||
|
for _, cmd := range c.commandOrder {
|
||||||
|
if cmd.isDefault {
|
||||||
|
defaults = append(defaults, cmd.name)
|
||||||
|
}
|
||||||
|
if seen[cmd.name] {
|
||||||
|
return fmt.Errorf("duplicate command %q", cmd.name)
|
||||||
|
}
|
||||||
|
seen[cmd.name] = true
|
||||||
|
for _, alias := range cmd.aliases {
|
||||||
|
if seen[alias] {
|
||||||
|
return fmt.Errorf("alias duplicates existing command %q", alias)
|
||||||
|
}
|
||||||
|
c.commands[alias] = cmd
|
||||||
|
}
|
||||||
|
if err := cmd.init(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(defaults) > 1 {
|
||||||
|
return fmt.Errorf("more than one default subcommand exists: %s", strings.Join(defaults, ", "))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cmdGroup) have() bool {
|
||||||
|
return len(c.commands) > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
type CmdClauseValidator func(*CmdClause) error
|
||||||
|
|
||||||
|
// A CmdClause is a single top-level command. It encapsulates a set of flags
|
||||||
|
// and either subcommands or positional arguments.
|
||||||
|
type CmdClause struct {
|
||||||
|
cmdMixin
|
||||||
|
app *Application
|
||||||
|
name string
|
||||||
|
aliases []string
|
||||||
|
help string
|
||||||
|
isDefault bool
|
||||||
|
validator CmdClauseValidator
|
||||||
|
hidden bool
|
||||||
|
completionAlts []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func newCommand(app *Application, name, help string) *CmdClause {
|
||||||
|
c := &CmdClause{
|
||||||
|
app: app,
|
||||||
|
name: name,
|
||||||
|
help: help,
|
||||||
|
}
|
||||||
|
c.flagGroup = newFlagGroup()
|
||||||
|
c.argGroup = newArgGroup()
|
||||||
|
c.cmdGroup = newCmdGroup(app)
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add an Alias for this command.
|
||||||
|
func (c *CmdClause) Alias(name string) *CmdClause {
|
||||||
|
c.aliases = append(c.aliases, name)
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate sets a validation function to run when parsing.
|
||||||
|
func (c *CmdClause) Validate(validator CmdClauseValidator) *CmdClause {
|
||||||
|
c.validator = validator
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CmdClause) FullCommand() string {
|
||||||
|
out := []string{c.name}
|
||||||
|
for p := c.parent; p != nil; p = p.parent {
|
||||||
|
out = append([]string{p.name}, out...)
|
||||||
|
}
|
||||||
|
return strings.Join(out, " ")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command adds a new sub-command.
|
||||||
|
func (c *CmdClause) Command(name, help string) *CmdClause {
|
||||||
|
cmd := c.addCommand(name, help)
|
||||||
|
cmd.parent = c
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default makes this command the default if commands don't match.
|
||||||
|
func (c *CmdClause) Default() *CmdClause {
|
||||||
|
c.isDefault = true
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CmdClause) Action(action Action) *CmdClause {
|
||||||
|
c.addAction(action)
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CmdClause) PreAction(action Action) *CmdClause {
|
||||||
|
c.addPreAction(action)
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CmdClause) init() error {
|
||||||
|
if err := c.flagGroup.init(c.app.defaultEnvarPrefix()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if c.argGroup.have() && c.cmdGroup.have() {
|
||||||
|
return fmt.Errorf("can't mix Arg()s with Command()s")
|
||||||
|
}
|
||||||
|
if err := c.argGroup.init(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := c.cmdGroup.init(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CmdClause) Hidden() *CmdClause {
|
||||||
|
c.hidden = true
|
||||||
|
return c
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
package kingpin
|
||||||
|
|
||||||
|
// HintAction is a function type who is expected to return a slice of possible
|
||||||
|
// command line arguments.
|
||||||
|
type HintAction func() []string
|
||||||
|
type completionsMixin struct {
|
||||||
|
hintActions []HintAction
|
||||||
|
builtinHintActions []HintAction
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *completionsMixin) addHintAction(action HintAction) {
|
||||||
|
a.hintActions = append(a.hintActions, action)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allow adding of HintActions which are added internally, ie, EnumVar
|
||||||
|
func (a *completionsMixin) addHintActionBuiltin(action HintAction) {
|
||||||
|
a.builtinHintActions = append(a.builtinHintActions, action)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *completionsMixin) resolveCompletions() []string {
|
||||||
|
var hints []string
|
||||||
|
|
||||||
|
options := a.builtinHintActions
|
||||||
|
if len(a.hintActions) > 0 {
|
||||||
|
// User specified their own hintActions. Use those instead.
|
||||||
|
options = a.hintActions
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, hintAction := range options {
|
||||||
|
hints = append(hints, hintAction()...)
|
||||||
|
}
|
||||||
|
return hints
|
||||||
|
}
|
|
@ -0,0 +1,68 @@
|
||||||
|
// Package kingpin provides command line interfaces like this:
|
||||||
|
//
|
||||||
|
// $ chat
|
||||||
|
// usage: chat [<flags>] <command> [<flags>] [<args> ...]
|
||||||
|
//
|
||||||
|
// Flags:
|
||||||
|
// --debug enable debug mode
|
||||||
|
// --help Show help.
|
||||||
|
// --server=127.0.0.1 server address
|
||||||
|
//
|
||||||
|
// Commands:
|
||||||
|
// help <command>
|
||||||
|
// Show help for a command.
|
||||||
|
//
|
||||||
|
// post [<flags>] <channel>
|
||||||
|
// Post a message to a channel.
|
||||||
|
//
|
||||||
|
// register <nick> <name>
|
||||||
|
// Register a new user.
|
||||||
|
//
|
||||||
|
// $ 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 code like this:
|
||||||
|
//
|
||||||
|
// package main
|
||||||
|
//
|
||||||
|
// import "gopkg.in/alecthomas/kingpin.v1"
|
||||||
|
//
|
||||||
|
// var (
|
||||||
|
// debug = kingpin.Flag("debug", "enable debug mode").Default("false").Bool()
|
||||||
|
// serverIP = kingpin.Flag("server", "server address").Default("127.0.0.1").IP()
|
||||||
|
//
|
||||||
|
// register = kingpin.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 = kingpin.Command("post", "Post a message to a channel.")
|
||||||
|
// postImage = post.Flag("image", "image to post").ExistingFile()
|
||||||
|
// postChannel = post.Arg("channel", "channel to post to").Required().String()
|
||||||
|
// postText = post.Arg("text", "text to post").String()
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// func main() {
|
||||||
|
// switch kingpin.Parse() {
|
||||||
|
// // Register user
|
||||||
|
// case "register":
|
||||||
|
// println(*registerNick)
|
||||||
|
//
|
||||||
|
// // Post message
|
||||||
|
// case "post":
|
||||||
|
// if *postImage != nil {
|
||||||
|
// }
|
||||||
|
// if *postText != "" {
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
package kingpin
|
|
@ -0,0 +1,45 @@
|
||||||
|
package kingpin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"regexp"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
envVarValuesSeparator = "\r?\n"
|
||||||
|
envVarValuesTrimmer = regexp.MustCompile(envVarValuesSeparator + "$")
|
||||||
|
envVarValuesSplitter = regexp.MustCompile(envVarValuesSeparator)
|
||||||
|
)
|
||||||
|
|
||||||
|
type envarMixin struct {
|
||||||
|
envar string
|
||||||
|
noEnvar bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *envarMixin) HasEnvarValue() bool {
|
||||||
|
return e.GetEnvarValue() != ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *envarMixin) GetEnvarValue() string {
|
||||||
|
if e.noEnvar || e.envar == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return os.Getenv(e.envar)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *envarMixin) GetSplitEnvarValue() []string {
|
||||||
|
values := make([]string, 0)
|
||||||
|
|
||||||
|
envarValue := e.GetEnvarValue()
|
||||||
|
if envarValue == "" {
|
||||||
|
return values
|
||||||
|
}
|
||||||
|
|
||||||
|
// Split by new line to extract multiple values, if any.
|
||||||
|
trimmed := envVarValuesTrimmer.ReplaceAllString(envarValue, "")
|
||||||
|
for _, value := range envVarValuesSplitter.Split(trimmed, -1) {
|
||||||
|
values = append(values, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
return values
|
||||||
|
}
|
|
@ -0,0 +1,308 @@
|
||||||
|
package kingpin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type flagGroup struct {
|
||||||
|
short map[string]*FlagClause
|
||||||
|
long map[string]*FlagClause
|
||||||
|
flagOrder []*FlagClause
|
||||||
|
}
|
||||||
|
|
||||||
|
func newFlagGroup() *flagGroup {
|
||||||
|
return &flagGroup{
|
||||||
|
short: map[string]*FlagClause{},
|
||||||
|
long: map[string]*FlagClause{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetFlag gets a flag definition.
|
||||||
|
//
|
||||||
|
// This allows existing flags to be modified after definition but before parsing. Useful for
|
||||||
|
// modular applications.
|
||||||
|
func (f *flagGroup) GetFlag(name string) *FlagClause {
|
||||||
|
return f.long[name]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flag defines a new flag with the given long name and help.
|
||||||
|
func (f *flagGroup) Flag(name, help string) *FlagClause {
|
||||||
|
flag := newFlag(name, help)
|
||||||
|
f.long[name] = flag
|
||||||
|
f.flagOrder = append(f.flagOrder, flag)
|
||||||
|
return flag
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *flagGroup) init(defaultEnvarPrefix string) error {
|
||||||
|
if err := f.checkDuplicates(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, flag := range f.long {
|
||||||
|
if defaultEnvarPrefix != "" && !flag.noEnvar && flag.envar == "" {
|
||||||
|
flag.envar = envarTransform(defaultEnvarPrefix + "_" + flag.name)
|
||||||
|
}
|
||||||
|
if err := flag.init(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if flag.shorthand != 0 {
|
||||||
|
f.short[string(flag.shorthand)] = flag
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *flagGroup) checkDuplicates() error {
|
||||||
|
seenShort := map[byte]bool{}
|
||||||
|
seenLong := map[string]bool{}
|
||||||
|
for _, flag := range f.flagOrder {
|
||||||
|
if flag.shorthand != 0 {
|
||||||
|
if _, ok := seenShort[flag.shorthand]; ok {
|
||||||
|
return fmt.Errorf("duplicate short flag -%c", flag.shorthand)
|
||||||
|
}
|
||||||
|
seenShort[flag.shorthand] = true
|
||||||
|
}
|
||||||
|
if _, ok := seenLong[flag.name]; ok {
|
||||||
|
return fmt.Errorf("duplicate long flag --%s", flag.name)
|
||||||
|
}
|
||||||
|
seenLong[flag.name] = true
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *flagGroup) parse(context *ParseContext) (*FlagClause, error) {
|
||||||
|
var token *Token
|
||||||
|
|
||||||
|
loop:
|
||||||
|
for {
|
||||||
|
token = context.Peek()
|
||||||
|
switch token.Type {
|
||||||
|
case TokenEOL:
|
||||||
|
break loop
|
||||||
|
|
||||||
|
case TokenLong, TokenShort:
|
||||||
|
flagToken := token
|
||||||
|
defaultValue := ""
|
||||||
|
var flag *FlagClause
|
||||||
|
var ok bool
|
||||||
|
invert := false
|
||||||
|
|
||||||
|
name := token.Value
|
||||||
|
if token.Type == TokenLong {
|
||||||
|
flag, ok = f.long[name]
|
||||||
|
if !ok {
|
||||||
|
if strings.HasPrefix(name, "no-") {
|
||||||
|
name = name[3:]
|
||||||
|
invert = true
|
||||||
|
}
|
||||||
|
flag, ok = f.long[name]
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("unknown long flag '%s'", flagToken)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
flag, ok = f.short[name]
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("unknown short flag '%s'", flagToken)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
context.Next()
|
||||||
|
|
||||||
|
fb, ok := flag.value.(boolFlag)
|
||||||
|
if ok && fb.IsBoolFlag() {
|
||||||
|
if invert {
|
||||||
|
defaultValue = "false"
|
||||||
|
} else {
|
||||||
|
defaultValue = "true"
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if invert {
|
||||||
|
context.Push(token)
|
||||||
|
return nil, fmt.Errorf("unknown long flag '%s'", flagToken)
|
||||||
|
}
|
||||||
|
token = context.Peek()
|
||||||
|
if token.Type != TokenArg {
|
||||||
|
context.Push(token)
|
||||||
|
return nil, fmt.Errorf("expected argument for flag '%s'", flagToken)
|
||||||
|
}
|
||||||
|
context.Next()
|
||||||
|
defaultValue = token.Value
|
||||||
|
}
|
||||||
|
|
||||||
|
context.matchedFlag(flag, defaultValue)
|
||||||
|
return flag, nil
|
||||||
|
|
||||||
|
default:
|
||||||
|
break loop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FlagClause is a fluid interface used to build flags.
|
||||||
|
type FlagClause struct {
|
||||||
|
parserMixin
|
||||||
|
actionMixin
|
||||||
|
completionsMixin
|
||||||
|
envarMixin
|
||||||
|
name string
|
||||||
|
shorthand byte
|
||||||
|
help string
|
||||||
|
defaultValues []string
|
||||||
|
placeholder string
|
||||||
|
hidden bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func newFlag(name, help string) *FlagClause {
|
||||||
|
f := &FlagClause{
|
||||||
|
name: name,
|
||||||
|
help: help,
|
||||||
|
}
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FlagClause) setDefault() error {
|
||||||
|
if f.HasEnvarValue() {
|
||||||
|
if v, ok := f.value.(repeatableFlag); !ok || !v.IsCumulative() {
|
||||||
|
// Use the value as-is
|
||||||
|
return f.value.Set(f.GetEnvarValue())
|
||||||
|
} else {
|
||||||
|
for _, value := range f.GetSplitEnvarValue() {
|
||||||
|
if err := f.value.Set(value); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(f.defaultValues) > 0 {
|
||||||
|
for _, defaultValue := range f.defaultValues {
|
||||||
|
if err := f.value.Set(defaultValue); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FlagClause) needsValue() bool {
|
||||||
|
haveDefault := len(f.defaultValues) > 0
|
||||||
|
return f.required && !(haveDefault || f.HasEnvarValue())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FlagClause) init() error {
|
||||||
|
if f.required && len(f.defaultValues) > 0 {
|
||||||
|
return fmt.Errorf("required flag '--%s' with default value that will never be used", f.name)
|
||||||
|
}
|
||||||
|
if f.value == nil {
|
||||||
|
return fmt.Errorf("no type defined for --%s (eg. .String())", f.name)
|
||||||
|
}
|
||||||
|
if v, ok := f.value.(repeatableFlag); (!ok || !v.IsCumulative()) && len(f.defaultValues) > 1 {
|
||||||
|
return fmt.Errorf("invalid default for '--%s', expecting single value", f.name)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dispatch to the given function after the flag is parsed and validated.
|
||||||
|
func (f *FlagClause) Action(action Action) *FlagClause {
|
||||||
|
f.addAction(action)
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FlagClause) PreAction(action Action) *FlagClause {
|
||||||
|
f.addPreAction(action)
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
|
// HintAction registers a HintAction (function) for the flag to provide completions
|
||||||
|
func (a *FlagClause) HintAction(action HintAction) *FlagClause {
|
||||||
|
a.addHintAction(action)
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
// HintOptions registers any number of options for the flag to provide completions
|
||||||
|
func (a *FlagClause) HintOptions(options ...string) *FlagClause {
|
||||||
|
a.addHintAction(func() []string {
|
||||||
|
return options
|
||||||
|
})
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *FlagClause) EnumVar(target *string, options ...string) {
|
||||||
|
a.parserMixin.EnumVar(target, options...)
|
||||||
|
a.addHintActionBuiltin(func() []string {
|
||||||
|
return options
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *FlagClause) Enum(options ...string) (target *string) {
|
||||||
|
a.addHintActionBuiltin(func() []string {
|
||||||
|
return options
|
||||||
|
})
|
||||||
|
return a.parserMixin.Enum(options...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default values for this flag. They *must* be parseable by the value of the flag.
|
||||||
|
func (f *FlagClause) Default(values ...string) *FlagClause {
|
||||||
|
f.defaultValues = values
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
|
// DEPRECATED: Use Envar(name) instead.
|
||||||
|
func (f *FlagClause) OverrideDefaultFromEnvar(envar string) *FlagClause {
|
||||||
|
return f.Envar(envar)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Envar overrides the default value(s) for a flag from an environment variable,
|
||||||
|
// if it is set. Several default values can be provided by using new lines to
|
||||||
|
// separate them.
|
||||||
|
func (f *FlagClause) Envar(name string) *FlagClause {
|
||||||
|
f.envar = name
|
||||||
|
f.noEnvar = false
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
|
// NoEnvar forces environment variable defaults to be disabled for this flag.
|
||||||
|
// Most useful in conjunction with app.DefaultEnvars().
|
||||||
|
func (f *FlagClause) NoEnvar() *FlagClause {
|
||||||
|
f.envar = ""
|
||||||
|
f.noEnvar = true
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
|
// PlaceHolder sets the place-holder string used for flag values in the help. The
|
||||||
|
// default behaviour is to use the value provided by Default() if provided,
|
||||||
|
// then fall back on the capitalized flag name.
|
||||||
|
func (f *FlagClause) PlaceHolder(placeholder string) *FlagClause {
|
||||||
|
f.placeholder = placeholder
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hidden hides a flag from usage but still allows it to be used.
|
||||||
|
func (f *FlagClause) Hidden() *FlagClause {
|
||||||
|
f.hidden = true
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
|
// Required makes the flag required. You can not provide a Default() value to a Required() flag.
|
||||||
|
func (f *FlagClause) Required() *FlagClause {
|
||||||
|
f.required = true
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
|
// Short sets the short flag name.
|
||||||
|
func (f *FlagClause) Short(name byte) *FlagClause {
|
||||||
|
f.shorthand = name
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bool makes this flag a boolean flag.
|
||||||
|
func (f *FlagClause) Bool() (target *bool) {
|
||||||
|
target = new(bool)
|
||||||
|
f.SetValue(newBoolValue(target))
|
||||||
|
return
|
||||||
|
}
|
|
@ -0,0 +1,94 @@
|
||||||
|
package kingpin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// CommandLine is the default Kingpin parser.
|
||||||
|
CommandLine = New(filepath.Base(os.Args[0]), "")
|
||||||
|
// Global help flag. Exposed for user customisation.
|
||||||
|
HelpFlag = CommandLine.HelpFlag
|
||||||
|
// Top-level help command. Exposed for user customisation. May be nil.
|
||||||
|
HelpCommand = CommandLine.HelpCommand
|
||||||
|
// Global version flag. Exposed for user customisation. May be nil.
|
||||||
|
VersionFlag = CommandLine.VersionFlag
|
||||||
|
)
|
||||||
|
|
||||||
|
// Command adds a new command to the default parser.
|
||||||
|
func Command(name, help string) *CmdClause {
|
||||||
|
return CommandLine.Command(name, help)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flag adds a new flag to the default parser.
|
||||||
|
func Flag(name, help string) *FlagClause {
|
||||||
|
return CommandLine.Flag(name, help)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Arg adds a new argument to the top-level of the default parser.
|
||||||
|
func Arg(name, help string) *ArgClause {
|
||||||
|
return CommandLine.Arg(name, help)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse and return the selected command. Will call the termination handler if
|
||||||
|
// an error is encountered.
|
||||||
|
func Parse() string {
|
||||||
|
selected := MustParse(CommandLine.Parse(os.Args[1:]))
|
||||||
|
if selected == "" && CommandLine.cmdGroup.have() {
|
||||||
|
Usage()
|
||||||
|
CommandLine.terminate(0)
|
||||||
|
}
|
||||||
|
return selected
|
||||||
|
}
|
||||||
|
|
||||||
|
// Errorf prints an error message to stderr.
|
||||||
|
func Errorf(format string, args ...interface{}) {
|
||||||
|
CommandLine.Errorf(format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fatalf prints an error message to stderr and exits.
|
||||||
|
func Fatalf(format string, args ...interface{}) {
|
||||||
|
CommandLine.Fatalf(format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FatalIfError prints an error and exits if err is not nil. The error is printed
|
||||||
|
// with the given prefix.
|
||||||
|
func FatalIfError(err error, format string, args ...interface{}) {
|
||||||
|
CommandLine.FatalIfError(err, format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FatalUsage prints an error message followed by usage information, then
|
||||||
|
// exits with a non-zero status.
|
||||||
|
func FatalUsage(format string, args ...interface{}) {
|
||||||
|
CommandLine.FatalUsage(format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FatalUsageContext writes a printf formatted error message to stderr, then
|
||||||
|
// usage information for the given ParseContext, before exiting.
|
||||||
|
func FatalUsageContext(context *ParseContext, format string, args ...interface{}) {
|
||||||
|
CommandLine.FatalUsageContext(context, format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Usage prints usage to stderr.
|
||||||
|
func Usage() {
|
||||||
|
CommandLine.Usage(os.Args[1:])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set global usage template to use (defaults to DefaultUsageTemplate).
|
||||||
|
func UsageTemplate(template string) *Application {
|
||||||
|
return CommandLine.UsageTemplate(template)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustParse can be used with app.Parse(args) to exit with an error if parsing fails.
|
||||||
|
func MustParse(command string, err error) string {
|
||||||
|
if err != nil {
|
||||||
|
Fatalf("%s, try --help", err)
|
||||||
|
}
|
||||||
|
return command
|
||||||
|
}
|
||||||
|
|
||||||
|
// Version adds a flag for displaying the application version number.
|
||||||
|
func Version(version string) *Application {
|
||||||
|
return CommandLine.Version(version)
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
// +build appengine !linux,!freebsd,!darwin,!dragonfly,!netbsd,!openbsd
|
||||||
|
|
||||||
|
package kingpin
|
||||||
|
|
||||||
|
import "io"
|
||||||
|
|
||||||
|
func guessWidth(w io.Writer) int {
|
||||||
|
return 80
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
// +build !appengine,linux freebsd darwin dragonfly netbsd openbsd
|
||||||
|
|
||||||
|
package kingpin
|
||||||
|
|
||||||
|
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),
|
||||||
|
uintptr(syscall.TIOCGWINSZ),
|
||||||
|
uintptr(unsafe.Pointer(&dimensions)),
|
||||||
|
0, 0, 0,
|
||||||
|
); err == 0 {
|
||||||
|
return int(dimensions[1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 80
|
||||||
|
}
|
|
@ -0,0 +1,227 @@
|
||||||
|
package kingpin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Data model for Kingpin command-line structure.
|
||||||
|
|
||||||
|
type FlagGroupModel struct {
|
||||||
|
Flags []*FlagModel
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FlagGroupModel) FlagSummary() string {
|
||||||
|
out := []string{}
|
||||||
|
count := 0
|
||||||
|
for _, flag := range f.Flags {
|
||||||
|
if flag.Name != "help" {
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
if flag.Required {
|
||||||
|
if flag.IsBoolFlag() {
|
||||||
|
out = append(out, fmt.Sprintf("--[no-]%s", flag.Name))
|
||||||
|
} else {
|
||||||
|
out = append(out, fmt.Sprintf("--%s=%s", flag.Name, flag.FormatPlaceHolder()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if count != len(out) {
|
||||||
|
out = append(out, "[<flags>]")
|
||||||
|
}
|
||||||
|
return strings.Join(out, " ")
|
||||||
|
}
|
||||||
|
|
||||||
|
type FlagModel struct {
|
||||||
|
Name string
|
||||||
|
Help string
|
||||||
|
Short rune
|
||||||
|
Default []string
|
||||||
|
Envar string
|
||||||
|
PlaceHolder string
|
||||||
|
Required bool
|
||||||
|
Hidden bool
|
||||||
|
Value Value
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FlagModel) String() string {
|
||||||
|
return f.Value.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FlagModel) IsBoolFlag() bool {
|
||||||
|
if fl, ok := f.Value.(boolFlag); ok {
|
||||||
|
return fl.IsBoolFlag()
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FlagModel) FormatPlaceHolder() string {
|
||||||
|
if f.PlaceHolder != "" {
|
||||||
|
return f.PlaceHolder
|
||||||
|
}
|
||||||
|
if len(f.Default) > 0 {
|
||||||
|
ellipsis := ""
|
||||||
|
if len(f.Default) > 1 {
|
||||||
|
ellipsis = "..."
|
||||||
|
}
|
||||||
|
if _, ok := f.Value.(*stringValue); ok {
|
||||||
|
return strconv.Quote(f.Default[0]) + ellipsis
|
||||||
|
}
|
||||||
|
return f.Default[0] + ellipsis
|
||||||
|
}
|
||||||
|
return strings.ToUpper(f.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
type ArgGroupModel struct {
|
||||||
|
Args []*ArgModel
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ArgGroupModel) ArgSummary() string {
|
||||||
|
depth := 0
|
||||||
|
out := []string{}
|
||||||
|
for _, arg := range a.Args {
|
||||||
|
h := "<" + arg.Name + ">"
|
||||||
|
if !arg.Required {
|
||||||
|
h = "[" + h
|
||||||
|
depth++
|
||||||
|
}
|
||||||
|
out = append(out, h)
|
||||||
|
}
|
||||||
|
out[len(out)-1] = out[len(out)-1] + strings.Repeat("]", depth)
|
||||||
|
return strings.Join(out, " ")
|
||||||
|
}
|
||||||
|
|
||||||
|
type ArgModel struct {
|
||||||
|
Name string
|
||||||
|
Help string
|
||||||
|
Default []string
|
||||||
|
Envar string
|
||||||
|
Required bool
|
||||||
|
Value Value
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ArgModel) String() string {
|
||||||
|
return a.Value.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
type CmdGroupModel struct {
|
||||||
|
Commands []*CmdModel
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CmdGroupModel) FlattenedCommands() (out []*CmdModel) {
|
||||||
|
for _, cmd := range c.Commands {
|
||||||
|
if len(cmd.Commands) == 0 {
|
||||||
|
out = append(out, cmd)
|
||||||
|
}
|
||||||
|
out = append(out, cmd.FlattenedCommands()...)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
type CmdModel struct {
|
||||||
|
Name string
|
||||||
|
Aliases []string
|
||||||
|
Help string
|
||||||
|
FullCommand string
|
||||||
|
Depth int
|
||||||
|
Hidden bool
|
||||||
|
Default bool
|
||||||
|
*FlagGroupModel
|
||||||
|
*ArgGroupModel
|
||||||
|
*CmdGroupModel
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CmdModel) String() string {
|
||||||
|
return c.FullCommand
|
||||||
|
}
|
||||||
|
|
||||||
|
type ApplicationModel struct {
|
||||||
|
Name string
|
||||||
|
Help string
|
||||||
|
Version string
|
||||||
|
Author string
|
||||||
|
*ArgGroupModel
|
||||||
|
*CmdGroupModel
|
||||||
|
*FlagGroupModel
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Application) Model() *ApplicationModel {
|
||||||
|
return &ApplicationModel{
|
||||||
|
Name: a.Name,
|
||||||
|
Help: a.Help,
|
||||||
|
Version: a.version,
|
||||||
|
Author: a.author,
|
||||||
|
FlagGroupModel: a.flagGroup.Model(),
|
||||||
|
ArgGroupModel: a.argGroup.Model(),
|
||||||
|
CmdGroupModel: a.cmdGroup.Model(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *argGroup) Model() *ArgGroupModel {
|
||||||
|
m := &ArgGroupModel{}
|
||||||
|
for _, arg := range a.args {
|
||||||
|
m.Args = append(m.Args, arg.Model())
|
||||||
|
}
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ArgClause) Model() *ArgModel {
|
||||||
|
return &ArgModel{
|
||||||
|
Name: a.name,
|
||||||
|
Help: a.help,
|
||||||
|
Default: a.defaultValues,
|
||||||
|
Envar: a.envar,
|
||||||
|
Required: a.required,
|
||||||
|
Value: a.value,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *flagGroup) Model() *FlagGroupModel {
|
||||||
|
m := &FlagGroupModel{}
|
||||||
|
for _, fl := range f.flagOrder {
|
||||||
|
m.Flags = append(m.Flags, fl.Model())
|
||||||
|
}
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FlagClause) Model() *FlagModel {
|
||||||
|
return &FlagModel{
|
||||||
|
Name: f.name,
|
||||||
|
Help: f.help,
|
||||||
|
Short: rune(f.shorthand),
|
||||||
|
Default: f.defaultValues,
|
||||||
|
Envar: f.envar,
|
||||||
|
PlaceHolder: f.placeholder,
|
||||||
|
Required: f.required,
|
||||||
|
Hidden: f.hidden,
|
||||||
|
Value: f.value,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cmdGroup) Model() *CmdGroupModel {
|
||||||
|
m := &CmdGroupModel{}
|
||||||
|
for _, cm := range c.commandOrder {
|
||||||
|
m.Commands = append(m.Commands, cm.Model())
|
||||||
|
}
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CmdClause) Model() *CmdModel {
|
||||||
|
depth := 0
|
||||||
|
for i := c; i != nil; i = i.parent {
|
||||||
|
depth++
|
||||||
|
}
|
||||||
|
return &CmdModel{
|
||||||
|
Name: c.name,
|
||||||
|
Aliases: c.aliases,
|
||||||
|
Help: c.help,
|
||||||
|
Depth: depth,
|
||||||
|
Hidden: c.hidden,
|
||||||
|
Default: c.isDefault,
|
||||||
|
FullCommand: c.FullCommand(),
|
||||||
|
FlagGroupModel: c.flagGroup.Model(),
|
||||||
|
ArgGroupModel: c.argGroup.Model(),
|
||||||
|
CmdGroupModel: c.cmdGroup.Model(),
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,380 @@
|
||||||
|
package kingpin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TokenType int
|
||||||
|
|
||||||
|
// Token types.
|
||||||
|
const (
|
||||||
|
TokenShort TokenType = iota
|
||||||
|
TokenLong
|
||||||
|
TokenArg
|
||||||
|
TokenError
|
||||||
|
TokenEOL
|
||||||
|
)
|
||||||
|
|
||||||
|
func (t TokenType) String() string {
|
||||||
|
switch t {
|
||||||
|
case TokenShort:
|
||||||
|
return "short flag"
|
||||||
|
case TokenLong:
|
||||||
|
return "long flag"
|
||||||
|
case TokenArg:
|
||||||
|
return "argument"
|
||||||
|
case TokenError:
|
||||||
|
return "error"
|
||||||
|
case TokenEOL:
|
||||||
|
return "<EOL>"
|
||||||
|
}
|
||||||
|
return "?"
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
TokenEOLMarker = Token{-1, TokenEOL, ""}
|
||||||
|
)
|
||||||
|
|
||||||
|
type Token struct {
|
||||||
|
Index int
|
||||||
|
Type TokenType
|
||||||
|
Value string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Token) Equal(o *Token) bool {
|
||||||
|
return t.Index == o.Index
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Token) IsFlag() bool {
|
||||||
|
return t.Type == TokenShort || t.Type == TokenLong
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Token) IsEOF() bool {
|
||||||
|
return t.Type == TokenEOL
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Token) String() string {
|
||||||
|
switch t.Type {
|
||||||
|
case TokenShort:
|
||||||
|
return "-" + t.Value
|
||||||
|
case TokenLong:
|
||||||
|
return "--" + t.Value
|
||||||
|
case TokenArg:
|
||||||
|
return t.Value
|
||||||
|
case TokenError:
|
||||||
|
return "error: " + t.Value
|
||||||
|
case TokenEOL:
|
||||||
|
return "<EOL>"
|
||||||
|
default:
|
||||||
|
panic("unhandled type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// A union of possible elements in a parse stack.
|
||||||
|
type ParseElement struct {
|
||||||
|
// Clause is either *CmdClause, *ArgClause or *FlagClause.
|
||||||
|
Clause interface{}
|
||||||
|
// Value is corresponding value for an ArgClause or FlagClause (if any).
|
||||||
|
Value *string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseContext holds the current context of the parser. When passed to
|
||||||
|
// Action() callbacks Elements will be fully populated with *FlagClause,
|
||||||
|
// *ArgClause and *CmdClause values and their corresponding arguments (if
|
||||||
|
// any).
|
||||||
|
type ParseContext struct {
|
||||||
|
SelectedCommand *CmdClause
|
||||||
|
ignoreDefault bool
|
||||||
|
argsOnly bool
|
||||||
|
peek []*Token
|
||||||
|
argi int // Index of current command-line arg we're processing.
|
||||||
|
args []string
|
||||||
|
rawArgs []string
|
||||||
|
flags *flagGroup
|
||||||
|
arguments *argGroup
|
||||||
|
argumenti int // Cursor into arguments
|
||||||
|
// Flags, arguments and commands encountered and collected during parse.
|
||||||
|
Elements []*ParseElement
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ParseContext) nextArg() *ArgClause {
|
||||||
|
if p.argumenti >= len(p.arguments.args) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
arg := p.arguments.args[p.argumenti]
|
||||||
|
if !arg.consumesRemainder() {
|
||||||
|
p.argumenti++
|
||||||
|
}
|
||||||
|
return arg
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ParseContext) next() {
|
||||||
|
p.argi++
|
||||||
|
p.args = p.args[1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasTrailingArgs returns true if there are unparsed command-line arguments.
|
||||||
|
// This can occur if the parser can not match remaining arguments.
|
||||||
|
func (p *ParseContext) HasTrailingArgs() bool {
|
||||||
|
return len(p.args) > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func tokenize(args []string, ignoreDefault bool) *ParseContext {
|
||||||
|
return &ParseContext{
|
||||||
|
ignoreDefault: ignoreDefault,
|
||||||
|
args: args,
|
||||||
|
rawArgs: args,
|
||||||
|
flags: newFlagGroup(),
|
||||||
|
arguments: newArgGroup(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ParseContext) mergeFlags(flags *flagGroup) {
|
||||||
|
for _, flag := range flags.flagOrder {
|
||||||
|
if flag.shorthand != 0 {
|
||||||
|
p.flags.short[string(flag.shorthand)] = flag
|
||||||
|
}
|
||||||
|
p.flags.long[flag.name] = flag
|
||||||
|
p.flags.flagOrder = append(p.flags.flagOrder, flag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ParseContext) mergeArgs(args *argGroup) {
|
||||||
|
for _, arg := range args.args {
|
||||||
|
p.arguments.args = append(p.arguments.args, arg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ParseContext) EOL() bool {
|
||||||
|
return p.Peek().Type == TokenEOL
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next token in the parse context.
|
||||||
|
func (p *ParseContext) Next() *Token {
|
||||||
|
if len(p.peek) > 0 {
|
||||||
|
return p.pop()
|
||||||
|
}
|
||||||
|
|
||||||
|
// End of tokens.
|
||||||
|
if len(p.args) == 0 {
|
||||||
|
return &Token{Index: p.argi, Type: TokenEOL}
|
||||||
|
}
|
||||||
|
|
||||||
|
arg := p.args[0]
|
||||||
|
p.next()
|
||||||
|
|
||||||
|
if p.argsOnly {
|
||||||
|
return &Token{p.argi, TokenArg, arg}
|
||||||
|
}
|
||||||
|
|
||||||
|
// All remaining args are passed directly.
|
||||||
|
if arg == "--" {
|
||||||
|
p.argsOnly = true
|
||||||
|
return p.Next()
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.HasPrefix(arg, "--") {
|
||||||
|
parts := strings.SplitN(arg[2:], "=", 2)
|
||||||
|
token := &Token{p.argi, TokenLong, parts[0]}
|
||||||
|
if len(parts) == 2 {
|
||||||
|
p.Push(&Token{p.argi, TokenArg, parts[1]})
|
||||||
|
}
|
||||||
|
return token
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.HasPrefix(arg, "-") {
|
||||||
|
if len(arg) == 1 {
|
||||||
|
return &Token{Index: p.argi, Type: TokenShort}
|
||||||
|
}
|
||||||
|
short := arg[1:2]
|
||||||
|
flag, ok := p.flags.short[short]
|
||||||
|
// Not a known short flag, we'll just return it anyway.
|
||||||
|
if !ok {
|
||||||
|
} else if fb, ok := flag.value.(boolFlag); ok && fb.IsBoolFlag() {
|
||||||
|
// Bool short flag.
|
||||||
|
} else {
|
||||||
|
// Short flag with combined argument: -fARG
|
||||||
|
token := &Token{p.argi, TokenShort, short}
|
||||||
|
if len(arg) > 2 {
|
||||||
|
p.Push(&Token{p.argi, TokenArg, arg[2:]})
|
||||||
|
}
|
||||||
|
return token
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(arg) > 2 {
|
||||||
|
p.args = append([]string{"-" + arg[2:]}, p.args...)
|
||||||
|
}
|
||||||
|
return &Token{p.argi, TokenShort, short}
|
||||||
|
} else if strings.HasPrefix(arg, "@") {
|
||||||
|
expanded, err := ExpandArgsFromFile(arg[1:])
|
||||||
|
if err != nil {
|
||||||
|
return &Token{p.argi, TokenError, err.Error()}
|
||||||
|
}
|
||||||
|
if p.argi >= len(p.args) {
|
||||||
|
p.args = append(p.args[:p.argi-1], expanded...)
|
||||||
|
} else {
|
||||||
|
p.args = append(p.args[:p.argi-1], append(expanded, p.args[p.argi+1:]...)...)
|
||||||
|
}
|
||||||
|
return p.Next()
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Token{p.argi, TokenArg, arg}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ParseContext) Peek() *Token {
|
||||||
|
if len(p.peek) == 0 {
|
||||||
|
return p.Push(p.Next())
|
||||||
|
}
|
||||||
|
return p.peek[len(p.peek)-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ParseContext) Push(token *Token) *Token {
|
||||||
|
p.peek = append(p.peek, token)
|
||||||
|
return token
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ParseContext) pop() *Token {
|
||||||
|
end := len(p.peek) - 1
|
||||||
|
token := p.peek[end]
|
||||||
|
p.peek = p.peek[0:end]
|
||||||
|
return token
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ParseContext) String() string {
|
||||||
|
return p.SelectedCommand.FullCommand()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ParseContext) matchedFlag(flag *FlagClause, value string) {
|
||||||
|
p.Elements = append(p.Elements, &ParseElement{Clause: flag, Value: &value})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ParseContext) matchedArg(arg *ArgClause, value string) {
|
||||||
|
p.Elements = append(p.Elements, &ParseElement{Clause: arg, Value: &value})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ParseContext) matchedCmd(cmd *CmdClause) {
|
||||||
|
p.Elements = append(p.Elements, &ParseElement{Clause: cmd})
|
||||||
|
p.mergeFlags(cmd.flagGroup)
|
||||||
|
p.mergeArgs(cmd.argGroup)
|
||||||
|
p.SelectedCommand = cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
// Expand arguments from a file. Lines starting with # will be treated as comments.
|
||||||
|
func ExpandArgsFromFile(filename string) (out []string, err error) {
|
||||||
|
r, err := os.Open(filename)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer r.Close()
|
||||||
|
scanner := bufio.NewScanner(r)
|
||||||
|
for scanner.Scan() {
|
||||||
|
line := scanner.Text()
|
||||||
|
if strings.HasPrefix(line, "#") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
out = append(out, line)
|
||||||
|
}
|
||||||
|
err = scanner.Err()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func parse(context *ParseContext, app *Application) (err error) {
|
||||||
|
context.mergeFlags(app.flagGroup)
|
||||||
|
context.mergeArgs(app.argGroup)
|
||||||
|
|
||||||
|
cmds := app.cmdGroup
|
||||||
|
ignoreDefault := context.ignoreDefault
|
||||||
|
|
||||||
|
loop:
|
||||||
|
for !context.EOL() {
|
||||||
|
token := context.Peek()
|
||||||
|
|
||||||
|
switch token.Type {
|
||||||
|
case TokenLong, TokenShort:
|
||||||
|
if flag, err := context.flags.parse(context); err != nil {
|
||||||
|
if !ignoreDefault {
|
||||||
|
if cmd := cmds.defaultSubcommand(); cmd != nil {
|
||||||
|
cmd.completionAlts = cmds.cmdNames()
|
||||||
|
context.matchedCmd(cmd)
|
||||||
|
cmds = cmd.cmdGroup
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
} else if flag == HelpFlag {
|
||||||
|
ignoreDefault = true
|
||||||
|
}
|
||||||
|
|
||||||
|
case TokenArg:
|
||||||
|
if cmds.have() {
|
||||||
|
selectedDefault := false
|
||||||
|
cmd, ok := cmds.commands[token.String()]
|
||||||
|
if !ok {
|
||||||
|
if !ignoreDefault {
|
||||||
|
if cmd = cmds.defaultSubcommand(); cmd != nil {
|
||||||
|
cmd.completionAlts = cmds.cmdNames()
|
||||||
|
selectedDefault = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if cmd == nil {
|
||||||
|
return fmt.Errorf("expected command but got %q", token)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if cmd == HelpCommand {
|
||||||
|
ignoreDefault = true
|
||||||
|
}
|
||||||
|
cmd.completionAlts = nil
|
||||||
|
context.matchedCmd(cmd)
|
||||||
|
cmds = cmd.cmdGroup
|
||||||
|
if !selectedDefault {
|
||||||
|
context.Next()
|
||||||
|
}
|
||||||
|
} else if context.arguments.have() {
|
||||||
|
if app.noInterspersed {
|
||||||
|
// no more flags
|
||||||
|
context.argsOnly = true
|
||||||
|
}
|
||||||
|
arg := context.nextArg()
|
||||||
|
if arg == nil {
|
||||||
|
break loop
|
||||||
|
}
|
||||||
|
context.matchedArg(arg, token.String())
|
||||||
|
context.Next()
|
||||||
|
} else {
|
||||||
|
break loop
|
||||||
|
}
|
||||||
|
|
||||||
|
case TokenEOL:
|
||||||
|
break loop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move to innermost default command.
|
||||||
|
for !ignoreDefault {
|
||||||
|
if cmd := cmds.defaultSubcommand(); cmd != nil {
|
||||||
|
cmd.completionAlts = cmds.cmdNames()
|
||||||
|
context.matchedCmd(cmd)
|
||||||
|
cmds = cmd.cmdGroup
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !context.EOL() {
|
||||||
|
return fmt.Errorf("unexpected %s", context.Peek())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set defaults for all remaining args.
|
||||||
|
for arg := context.nextArg(); arg != nil && !arg.consumesRemainder(); arg = context.nextArg() {
|
||||||
|
for _, defaultValue := range arg.defaultValues {
|
||||||
|
if err := arg.value.Set(defaultValue); err != nil {
|
||||||
|
return fmt.Errorf("invalid default value '%s' for argument '%s'", defaultValue, arg.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
|
@ -0,0 +1,212 @@
|
||||||
|
package kingpin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/alecthomas/units"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Settings interface {
|
||||||
|
SetValue(value Value)
|
||||||
|
}
|
||||||
|
|
||||||
|
type parserMixin struct {
|
||||||
|
value Value
|
||||||
|
required bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parserMixin) SetValue(value Value) {
|
||||||
|
p.value = value
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringMap provides key=value parsing into a map.
|
||||||
|
func (p *parserMixin) StringMap() (target *map[string]string) {
|
||||||
|
target = &(map[string]string{})
|
||||||
|
p.StringMapVar(target)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Duration sets the parser to a time.Duration parser.
|
||||||
|
func (p *parserMixin) Duration() (target *time.Duration) {
|
||||||
|
target = new(time.Duration)
|
||||||
|
p.DurationVar(target)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bytes parses numeric byte units. eg. 1.5KB
|
||||||
|
func (p *parserMixin) Bytes() (target *units.Base2Bytes) {
|
||||||
|
target = new(units.Base2Bytes)
|
||||||
|
p.BytesVar(target)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// IP sets the parser to a net.IP parser.
|
||||||
|
func (p *parserMixin) IP() (target *net.IP) {
|
||||||
|
target = new(net.IP)
|
||||||
|
p.IPVar(target)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// TCP (host:port) address.
|
||||||
|
func (p *parserMixin) TCP() (target **net.TCPAddr) {
|
||||||
|
target = new(*net.TCPAddr)
|
||||||
|
p.TCPVar(target)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// TCPVar (host:port) address.
|
||||||
|
func (p *parserMixin) TCPVar(target **net.TCPAddr) {
|
||||||
|
p.SetValue(newTCPAddrValue(target))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExistingFile sets the parser to one that requires and returns an existing file.
|
||||||
|
func (p *parserMixin) ExistingFile() (target *string) {
|
||||||
|
target = new(string)
|
||||||
|
p.ExistingFileVar(target)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExistingDir sets the parser to one that requires and returns an existing directory.
|
||||||
|
func (p *parserMixin) ExistingDir() (target *string) {
|
||||||
|
target = new(string)
|
||||||
|
p.ExistingDirVar(target)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExistingFileOrDir sets the parser to one that requires and returns an existing file OR directory.
|
||||||
|
func (p *parserMixin) ExistingFileOrDir() (target *string) {
|
||||||
|
target = new(string)
|
||||||
|
p.ExistingFileOrDirVar(target)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// File returns an os.File against an existing file.
|
||||||
|
func (p *parserMixin) File() (target **os.File) {
|
||||||
|
target = new(*os.File)
|
||||||
|
p.FileVar(target)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// File attempts to open a File with os.OpenFile(flag, perm).
|
||||||
|
func (p *parserMixin) OpenFile(flag int, perm os.FileMode) (target **os.File) {
|
||||||
|
target = new(*os.File)
|
||||||
|
p.OpenFileVar(target, flag, perm)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// URL provides a valid, parsed url.URL.
|
||||||
|
func (p *parserMixin) URL() (target **url.URL) {
|
||||||
|
target = new(*url.URL)
|
||||||
|
p.URLVar(target)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringMap provides key=value parsing into a map.
|
||||||
|
func (p *parserMixin) StringMapVar(target *map[string]string) {
|
||||||
|
p.SetValue(newStringMapValue(target))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float sets the parser to a float64 parser.
|
||||||
|
func (p *parserMixin) Float() (target *float64) {
|
||||||
|
return p.Float64()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float sets the parser to a float64 parser.
|
||||||
|
func (p *parserMixin) FloatVar(target *float64) {
|
||||||
|
p.Float64Var(target)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Duration sets the parser to a time.Duration parser.
|
||||||
|
func (p *parserMixin) DurationVar(target *time.Duration) {
|
||||||
|
p.SetValue(newDurationValue(target))
|
||||||
|
}
|
||||||
|
|
||||||
|
// BytesVar parses numeric byte units. eg. 1.5KB
|
||||||
|
func (p *parserMixin) BytesVar(target *units.Base2Bytes) {
|
||||||
|
p.SetValue(newBytesValue(target))
|
||||||
|
}
|
||||||
|
|
||||||
|
// IP sets the parser to a net.IP parser.
|
||||||
|
func (p *parserMixin) IPVar(target *net.IP) {
|
||||||
|
p.SetValue(newIPValue(target))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExistingFile sets the parser to one that requires and returns an existing file.
|
||||||
|
func (p *parserMixin) ExistingFileVar(target *string) {
|
||||||
|
p.SetValue(newExistingFileValue(target))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExistingDir sets the parser to one that requires and returns an existing directory.
|
||||||
|
func (p *parserMixin) ExistingDirVar(target *string) {
|
||||||
|
p.SetValue(newExistingDirValue(target))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExistingDir sets the parser to one that requires and returns an existing directory.
|
||||||
|
func (p *parserMixin) ExistingFileOrDirVar(target *string) {
|
||||||
|
p.SetValue(newExistingFileOrDirValue(target))
|
||||||
|
}
|
||||||
|
|
||||||
|
// FileVar opens an existing file.
|
||||||
|
func (p *parserMixin) FileVar(target **os.File) {
|
||||||
|
p.SetValue(newFileValue(target, os.O_RDONLY, 0))
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenFileVar calls os.OpenFile(flag, perm)
|
||||||
|
func (p *parserMixin) OpenFileVar(target **os.File, flag int, perm os.FileMode) {
|
||||||
|
p.SetValue(newFileValue(target, flag, perm))
|
||||||
|
}
|
||||||
|
|
||||||
|
// URL provides a valid, parsed url.URL.
|
||||||
|
func (p *parserMixin) URLVar(target **url.URL) {
|
||||||
|
p.SetValue(newURLValue(target))
|
||||||
|
}
|
||||||
|
|
||||||
|
// URLList provides a parsed list of url.URL values.
|
||||||
|
func (p *parserMixin) URLList() (target *[]*url.URL) {
|
||||||
|
target = new([]*url.URL)
|
||||||
|
p.URLListVar(target)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// URLListVar provides a parsed list of url.URL values.
|
||||||
|
func (p *parserMixin) URLListVar(target *[]*url.URL) {
|
||||||
|
p.SetValue(newURLListValue(target))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enum allows a value from a set of options.
|
||||||
|
func (p *parserMixin) Enum(options ...string) (target *string) {
|
||||||
|
target = new(string)
|
||||||
|
p.EnumVar(target, options...)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnumVar allows a value from a set of options.
|
||||||
|
func (p *parserMixin) EnumVar(target *string, options ...string) {
|
||||||
|
p.SetValue(newEnumFlag(target, options...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enums allows a set of values from a set of options.
|
||||||
|
func (p *parserMixin) Enums(options ...string) (target *[]string) {
|
||||||
|
target = new([]string)
|
||||||
|
p.EnumsVar(target, options...)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnumVar allows a value from a set of options.
|
||||||
|
func (p *parserMixin) EnumsVar(target *[]string, options ...string) {
|
||||||
|
p.SetValue(newEnumsFlag(target, options...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Counter increments a number each time it is encountered.
|
||||||
|
func (p *parserMixin) Counter() (target *int) {
|
||||||
|
target = new(int)
|
||||||
|
p.CounterVar(target)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parserMixin) CounterVar(target *int) {
|
||||||
|
p.SetValue(newCounterValue(target))
|
||||||
|
}
|
|
@ -0,0 +1,262 @@
|
||||||
|
package kingpin
|
||||||
|
|
||||||
|
// Default usage template.
|
||||||
|
var DefaultUsageTemplate = `{{define "FormatCommand"}}\
|
||||||
|
{{if .FlagSummary}} {{.FlagSummary}}{{end}}\
|
||||||
|
{{range .Args}} {{if not .Required}}[{{end}}<{{.Name}}>{{if .Value|IsCumulative}}...{{end}}{{if not .Required}}]{{end}}{{end}}\
|
||||||
|
{{end}}\
|
||||||
|
|
||||||
|
{{define "FormatCommands"}}\
|
||||||
|
{{range .FlattenedCommands}}\
|
||||||
|
{{if not .Hidden}}\
|
||||||
|
{{.FullCommand}}{{if .Default}}*{{end}}{{template "FormatCommand" .}}
|
||||||
|
{{.Help|Wrap 4}}
|
||||||
|
{{end}}\
|
||||||
|
{{end}}\
|
||||||
|
{{end}}\
|
||||||
|
|
||||||
|
{{define "FormatUsage"}}\
|
||||||
|
{{template "FormatCommand" .}}{{if .Commands}} <command> [<args> ...]{{end}}
|
||||||
|
{{if .Help}}
|
||||||
|
{{.Help|Wrap 0}}\
|
||||||
|
{{end}}\
|
||||||
|
|
||||||
|
{{end}}\
|
||||||
|
|
||||||
|
{{if .Context.SelectedCommand}}\
|
||||||
|
usage: {{.App.Name}} {{.Context.SelectedCommand}}{{template "FormatUsage" .Context.SelectedCommand}}
|
||||||
|
{{else}}\
|
||||||
|
usage: {{.App.Name}}{{template "FormatUsage" .App}}
|
||||||
|
{{end}}\
|
||||||
|
{{if .Context.Flags}}\
|
||||||
|
Flags:
|
||||||
|
{{.Context.Flags|FlagsToTwoColumns|FormatTwoColumns}}
|
||||||
|
{{end}}\
|
||||||
|
{{if .Context.Args}}\
|
||||||
|
Args:
|
||||||
|
{{.Context.Args|ArgsToTwoColumns|FormatTwoColumns}}
|
||||||
|
{{end}}\
|
||||||
|
{{if .Context.SelectedCommand}}\
|
||||||
|
{{if len .Context.SelectedCommand.Commands}}\
|
||||||
|
Subcommands:
|
||||||
|
{{template "FormatCommands" .Context.SelectedCommand}}
|
||||||
|
{{end}}\
|
||||||
|
{{else if .App.Commands}}\
|
||||||
|
Commands:
|
||||||
|
{{template "FormatCommands" .App}}
|
||||||
|
{{end}}\
|
||||||
|
`
|
||||||
|
|
||||||
|
// Usage template where command's optional flags are listed separately
|
||||||
|
var SeparateOptionalFlagsUsageTemplate = `{{define "FormatCommand"}}\
|
||||||
|
{{if .FlagSummary}} {{.FlagSummary}}{{end}}\
|
||||||
|
{{range .Args}} {{if not .Required}}[{{end}}<{{.Name}}>{{if .Value|IsCumulative}}...{{end}}{{if not .Required}}]{{end}}{{end}}\
|
||||||
|
{{end}}\
|
||||||
|
|
||||||
|
{{define "FormatCommands"}}\
|
||||||
|
{{range .FlattenedCommands}}\
|
||||||
|
{{if not .Hidden}}\
|
||||||
|
{{.FullCommand}}{{if .Default}}*{{end}}{{template "FormatCommand" .}}
|
||||||
|
{{.Help|Wrap 4}}
|
||||||
|
{{end}}\
|
||||||
|
{{end}}\
|
||||||
|
{{end}}\
|
||||||
|
|
||||||
|
{{define "FormatUsage"}}\
|
||||||
|
{{template "FormatCommand" .}}{{if .Commands}} <command> [<args> ...]{{end}}
|
||||||
|
{{if .Help}}
|
||||||
|
{{.Help|Wrap 0}}\
|
||||||
|
{{end}}\
|
||||||
|
|
||||||
|
{{end}}\
|
||||||
|
{{if .Context.SelectedCommand}}\
|
||||||
|
usage: {{.App.Name}} {{.Context.SelectedCommand}}{{template "FormatUsage" .Context.SelectedCommand}}
|
||||||
|
{{else}}\
|
||||||
|
usage: {{.App.Name}}{{template "FormatUsage" .App}}
|
||||||
|
{{end}}\
|
||||||
|
|
||||||
|
{{if .Context.Flags|RequiredFlags}}\
|
||||||
|
Required flags:
|
||||||
|
{{.Context.Flags|RequiredFlags|FlagsToTwoColumns|FormatTwoColumns}}
|
||||||
|
{{end}}\
|
||||||
|
{{if .Context.Flags|OptionalFlags}}\
|
||||||
|
Optional flags:
|
||||||
|
{{.Context.Flags|OptionalFlags|FlagsToTwoColumns|FormatTwoColumns}}
|
||||||
|
{{end}}\
|
||||||
|
{{if .Context.Args}}\
|
||||||
|
Args:
|
||||||
|
{{.Context.Args|ArgsToTwoColumns|FormatTwoColumns}}
|
||||||
|
{{end}}\
|
||||||
|
{{if .Context.SelectedCommand}}\
|
||||||
|
Subcommands:
|
||||||
|
{{if .Context.SelectedCommand.Commands}}\
|
||||||
|
{{template "FormatCommands" .Context.SelectedCommand}}
|
||||||
|
{{end}}\
|
||||||
|
{{else if .App.Commands}}\
|
||||||
|
Commands:
|
||||||
|
{{template "FormatCommands" .App}}
|
||||||
|
{{end}}\
|
||||||
|
`
|
||||||
|
|
||||||
|
// Usage template with compactly formatted commands.
|
||||||
|
var CompactUsageTemplate = `{{define "FormatCommand"}}\
|
||||||
|
{{if .FlagSummary}} {{.FlagSummary}}{{end}}\
|
||||||
|
{{range .Args}} {{if not .Required}}[{{end}}<{{.Name}}>{{if .Value|IsCumulative}}...{{end}}{{if not .Required}}]{{end}}{{end}}\
|
||||||
|
{{end}}\
|
||||||
|
|
||||||
|
{{define "FormatCommandList"}}\
|
||||||
|
{{range .}}\
|
||||||
|
{{if not .Hidden}}\
|
||||||
|
{{.Depth|Indent}}{{.Name}}{{if .Default}}*{{end}}{{template "FormatCommand" .}}
|
||||||
|
{{end}}\
|
||||||
|
{{template "FormatCommandList" .Commands}}\
|
||||||
|
{{end}}\
|
||||||
|
{{end}}\
|
||||||
|
|
||||||
|
{{define "FormatUsage"}}\
|
||||||
|
{{template "FormatCommand" .}}{{if .Commands}} <command> [<args> ...]{{end}}
|
||||||
|
{{if .Help}}
|
||||||
|
{{.Help|Wrap 0}}\
|
||||||
|
{{end}}\
|
||||||
|
|
||||||
|
{{end}}\
|
||||||
|
|
||||||
|
{{if .Context.SelectedCommand}}\
|
||||||
|
usage: {{.App.Name}} {{.Context.SelectedCommand}}{{template "FormatUsage" .Context.SelectedCommand}}
|
||||||
|
{{else}}\
|
||||||
|
usage: {{.App.Name}}{{template "FormatUsage" .App}}
|
||||||
|
{{end}}\
|
||||||
|
{{if .Context.Flags}}\
|
||||||
|
Flags:
|
||||||
|
{{.Context.Flags|FlagsToTwoColumns|FormatTwoColumns}}
|
||||||
|
{{end}}\
|
||||||
|
{{if .Context.Args}}\
|
||||||
|
Args:
|
||||||
|
{{.Context.Args|ArgsToTwoColumns|FormatTwoColumns}}
|
||||||
|
{{end}}\
|
||||||
|
{{if .Context.SelectedCommand}}\
|
||||||
|
{{if .Context.SelectedCommand.Commands}}\
|
||||||
|
Commands:
|
||||||
|
{{.Context.SelectedCommand}}
|
||||||
|
{{template "FormatCommandList" .Context.SelectedCommand.Commands}}
|
||||||
|
{{end}}\
|
||||||
|
{{else if .App.Commands}}\
|
||||||
|
Commands:
|
||||||
|
{{template "FormatCommandList" .App.Commands}}
|
||||||
|
{{end}}\
|
||||||
|
`
|
||||||
|
|
||||||
|
var ManPageTemplate = `{{define "FormatFlags"}}\
|
||||||
|
{{range .Flags}}\
|
||||||
|
{{if not .Hidden}}\
|
||||||
|
.TP
|
||||||
|
\fB{{if .Short}}-{{.Short|Char}}, {{end}}--{{.Name}}{{if not .IsBoolFlag}}={{.FormatPlaceHolder}}{{end}}\\fR
|
||||||
|
{{.Help}}
|
||||||
|
{{end}}\
|
||||||
|
{{end}}\
|
||||||
|
{{end}}\
|
||||||
|
|
||||||
|
{{define "FormatCommand"}}\
|
||||||
|
{{if .FlagSummary}} {{.FlagSummary}}{{end}}\
|
||||||
|
{{range .Args}} {{if not .Required}}[{{end}}<{{.Name}}{{if .Default}}*{{end}}>{{if .Value|IsCumulative}}...{{end}}{{if not .Required}}]{{end}}{{end}}\
|
||||||
|
{{end}}\
|
||||||
|
|
||||||
|
{{define "FormatCommands"}}\
|
||||||
|
{{range .FlattenedCommands}}\
|
||||||
|
{{if not .Hidden}}\
|
||||||
|
.SS
|
||||||
|
\fB{{.FullCommand}}{{template "FormatCommand" .}}\\fR
|
||||||
|
.PP
|
||||||
|
{{.Help}}
|
||||||
|
{{template "FormatFlags" .}}\
|
||||||
|
{{end}}\
|
||||||
|
{{end}}\
|
||||||
|
{{end}}\
|
||||||
|
|
||||||
|
{{define "FormatUsage"}}\
|
||||||
|
{{template "FormatCommand" .}}{{if .Commands}} <command> [<args> ...]{{end}}\\fR
|
||||||
|
{{end}}\
|
||||||
|
|
||||||
|
.TH {{.App.Name}} 1 {{.App.Version}} "{{.App.Author}}"
|
||||||
|
.SH "NAME"
|
||||||
|
{{.App.Name}}
|
||||||
|
.SH "SYNOPSIS"
|
||||||
|
.TP
|
||||||
|
\fB{{.App.Name}}{{template "FormatUsage" .App}}
|
||||||
|
.SH "DESCRIPTION"
|
||||||
|
{{.App.Help}}
|
||||||
|
.SH "OPTIONS"
|
||||||
|
{{template "FormatFlags" .App}}\
|
||||||
|
{{if .App.Commands}}\
|
||||||
|
.SH "COMMANDS"
|
||||||
|
{{template "FormatCommands" .App}}\
|
||||||
|
{{end}}\
|
||||||
|
`
|
||||||
|
|
||||||
|
// Default usage template.
|
||||||
|
var LongHelpTemplate = `{{define "FormatCommand"}}\
|
||||||
|
{{if .FlagSummary}} {{.FlagSummary}}{{end}}\
|
||||||
|
{{range .Args}} {{if not .Required}}[{{end}}<{{.Name}}>{{if .Value|IsCumulative}}...{{end}}{{if not .Required}}]{{end}}{{end}}\
|
||||||
|
{{end}}\
|
||||||
|
|
||||||
|
{{define "FormatCommands"}}\
|
||||||
|
{{range .FlattenedCommands}}\
|
||||||
|
{{if not .Hidden}}\
|
||||||
|
{{.FullCommand}}{{template "FormatCommand" .}}
|
||||||
|
{{.Help|Wrap 4}}
|
||||||
|
{{with .Flags|FlagsToTwoColumns}}{{FormatTwoColumnsWithIndent . 4 2}}{{end}}
|
||||||
|
{{end}}\
|
||||||
|
{{end}}\
|
||||||
|
{{end}}\
|
||||||
|
|
||||||
|
{{define "FormatUsage"}}\
|
||||||
|
{{template "FormatCommand" .}}{{if .Commands}} <command> [<args> ...]{{end}}
|
||||||
|
{{if .Help}}
|
||||||
|
{{.Help|Wrap 0}}\
|
||||||
|
{{end}}\
|
||||||
|
|
||||||
|
{{end}}\
|
||||||
|
|
||||||
|
usage: {{.App.Name}}{{template "FormatUsage" .App}}
|
||||||
|
{{if .Context.Flags}}\
|
||||||
|
Flags:
|
||||||
|
{{.Context.Flags|FlagsToTwoColumns|FormatTwoColumns}}
|
||||||
|
{{end}}\
|
||||||
|
{{if .Context.Args}}\
|
||||||
|
Args:
|
||||||
|
{{.Context.Args|ArgsToTwoColumns|FormatTwoColumns}}
|
||||||
|
{{end}}\
|
||||||
|
{{if .App.Commands}}\
|
||||||
|
Commands:
|
||||||
|
{{template "FormatCommands" .App}}
|
||||||
|
{{end}}\
|
||||||
|
`
|
||||||
|
|
||||||
|
var BashCompletionTemplate = `
|
||||||
|
_{{.App.Name}}_bash_autocomplete() {
|
||||||
|
local cur prev opts base
|
||||||
|
COMPREPLY=()
|
||||||
|
cur="${COMP_WORDS[COMP_CWORD]}"
|
||||||
|
opts=$( ${COMP_WORDS[0]} --completion-bash ${COMP_WORDS[@]:1:$COMP_CWORD} )
|
||||||
|
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
complete -F _{{.App.Name}}_bash_autocomplete {{.App.Name}}
|
||||||
|
|
||||||
|
`
|
||||||
|
|
||||||
|
var ZshCompletionTemplate = `
|
||||||
|
#compdef {{.App.Name}}
|
||||||
|
autoload -U compinit && compinit
|
||||||
|
autoload -U bashcompinit && bashcompinit
|
||||||
|
|
||||||
|
_{{.App.Name}}_bash_autocomplete() {
|
||||||
|
local cur prev opts base
|
||||||
|
COMPREPLY=()
|
||||||
|
cur="${COMP_WORDS[COMP_CWORD]}"
|
||||||
|
opts=$( ${COMP_WORDS[0]} --completion-bash ${COMP_WORDS[@]:1:$COMP_CWORD} )
|
||||||
|
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
complete -F _{{.App.Name}}_bash_autocomplete {{.App.Name}}
|
||||||
|
`
|
|
@ -0,0 +1,211 @@
|
||||||
|
package kingpin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"go/doc"
|
||||||
|
"io"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/alecthomas/template"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
preIndent = " "
|
||||||
|
)
|
||||||
|
|
||||||
|
func formatTwoColumns(w io.Writer, indent, padding, width int, rows [][2]string) {
|
||||||
|
// Find size of first column.
|
||||||
|
s := 0
|
||||||
|
for _, row := range rows {
|
||||||
|
if c := len(row[0]); c > s && c < 30 {
|
||||||
|
s = c
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
indentStr := strings.Repeat(" ", indent)
|
||||||
|
offsetStr := strings.Repeat(" ", s+padding)
|
||||||
|
|
||||||
|
for _, row := range rows {
|
||||||
|
buf := bytes.NewBuffer(nil)
|
||||||
|
doc.ToText(buf, row[1], "", preIndent, width-s-padding-indent)
|
||||||
|
lines := strings.Split(strings.TrimRight(buf.String(), "\n"), "\n")
|
||||||
|
fmt.Fprintf(w, "%s%-*s%*s", indentStr, s, row[0], padding, "")
|
||||||
|
if len(row[0]) >= 30 {
|
||||||
|
fmt.Fprintf(w, "\n%s%s", indentStr, offsetStr)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(w, "%s\n", lines[0])
|
||||||
|
for _, line := range lines[1:] {
|
||||||
|
fmt.Fprintf(w, "%s%s%s\n", indentStr, offsetStr, line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Usage writes application usage to w. It parses args to determine
|
||||||
|
// appropriate help context, such as which command to show help for.
|
||||||
|
func (a *Application) Usage(args []string) {
|
||||||
|
context, err := a.parseContext(true, args)
|
||||||
|
a.FatalIfError(err, "")
|
||||||
|
if err := a.UsageForContextWithTemplate(context, 2, a.usageTemplate); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatAppUsage(app *ApplicationModel) string {
|
||||||
|
s := []string{app.Name}
|
||||||
|
if len(app.Flags) > 0 {
|
||||||
|
s = append(s, app.FlagSummary())
|
||||||
|
}
|
||||||
|
if len(app.Args) > 0 {
|
||||||
|
s = append(s, app.ArgSummary())
|
||||||
|
}
|
||||||
|
return strings.Join(s, " ")
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatCmdUsage(app *ApplicationModel, cmd *CmdModel) string {
|
||||||
|
s := []string{app.Name, cmd.String()}
|
||||||
|
if len(app.Flags) > 0 {
|
||||||
|
s = append(s, app.FlagSummary())
|
||||||
|
}
|
||||||
|
if len(app.Args) > 0 {
|
||||||
|
s = append(s, app.ArgSummary())
|
||||||
|
}
|
||||||
|
return strings.Join(s, " ")
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatFlag(haveShort bool, flag *FlagModel) string {
|
||||||
|
flagString := ""
|
||||||
|
if flag.Short != 0 {
|
||||||
|
flagString += fmt.Sprintf("-%c, --%s", flag.Short, flag.Name)
|
||||||
|
} else {
|
||||||
|
if haveShort {
|
||||||
|
flagString += fmt.Sprintf(" --%s", flag.Name)
|
||||||
|
} else {
|
||||||
|
flagString += fmt.Sprintf("--%s", flag.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !flag.IsBoolFlag() {
|
||||||
|
flagString += fmt.Sprintf("=%s", flag.FormatPlaceHolder())
|
||||||
|
}
|
||||||
|
if v, ok := flag.Value.(repeatableFlag); ok && v.IsCumulative() {
|
||||||
|
flagString += " ..."
|
||||||
|
}
|
||||||
|
return flagString
|
||||||
|
}
|
||||||
|
|
||||||
|
type templateParseContext struct {
|
||||||
|
SelectedCommand *CmdModel
|
||||||
|
*FlagGroupModel
|
||||||
|
*ArgGroupModel
|
||||||
|
}
|
||||||
|
|
||||||
|
type templateContext struct {
|
||||||
|
App *ApplicationModel
|
||||||
|
Width int
|
||||||
|
Context *templateParseContext
|
||||||
|
}
|
||||||
|
|
||||||
|
// UsageForContext displays usage information from a ParseContext (obtained from
|
||||||
|
// Application.ParseContext() or Action(f) callbacks).
|
||||||
|
func (a *Application) UsageForContext(context *ParseContext) error {
|
||||||
|
return a.UsageForContextWithTemplate(context, 2, a.usageTemplate)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UsageForContextWithTemplate is the base usage function. You generally don't need to use this.
|
||||||
|
func (a *Application) UsageForContextWithTemplate(context *ParseContext, indent int, tmpl string) error {
|
||||||
|
width := guessWidth(a.usageWriter)
|
||||||
|
funcs := template.FuncMap{
|
||||||
|
"Indent": func(level int) string {
|
||||||
|
return strings.Repeat(" ", level*indent)
|
||||||
|
},
|
||||||
|
"Wrap": func(indent int, s string) string {
|
||||||
|
buf := bytes.NewBuffer(nil)
|
||||||
|
indentText := strings.Repeat(" ", indent)
|
||||||
|
doc.ToText(buf, s, indentText, " "+indentText, width-indent)
|
||||||
|
return buf.String()
|
||||||
|
},
|
||||||
|
"FormatFlag": formatFlag,
|
||||||
|
"FlagsToTwoColumns": func(f []*FlagModel) [][2]string {
|
||||||
|
rows := [][2]string{}
|
||||||
|
haveShort := false
|
||||||
|
for _, flag := range f {
|
||||||
|
if flag.Short != 0 {
|
||||||
|
haveShort = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, flag := range f {
|
||||||
|
if !flag.Hidden {
|
||||||
|
rows = append(rows, [2]string{formatFlag(haveShort, flag), flag.Help})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rows
|
||||||
|
},
|
||||||
|
"RequiredFlags": func(f []*FlagModel) []*FlagModel {
|
||||||
|
requiredFlags := []*FlagModel{}
|
||||||
|
for _, flag := range f {
|
||||||
|
if flag.Required {
|
||||||
|
requiredFlags = append(requiredFlags, flag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return requiredFlags
|
||||||
|
},
|
||||||
|
"OptionalFlags": func(f []*FlagModel) []*FlagModel {
|
||||||
|
optionalFlags := []*FlagModel{}
|
||||||
|
for _, flag := range f {
|
||||||
|
if !flag.Required {
|
||||||
|
optionalFlags = append(optionalFlags, flag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return optionalFlags
|
||||||
|
},
|
||||||
|
"ArgsToTwoColumns": func(a []*ArgModel) [][2]string {
|
||||||
|
rows := [][2]string{}
|
||||||
|
for _, arg := range a {
|
||||||
|
s := "<" + arg.Name + ">"
|
||||||
|
if !arg.Required {
|
||||||
|
s = "[" + s + "]"
|
||||||
|
}
|
||||||
|
rows = append(rows, [2]string{s, arg.Help})
|
||||||
|
}
|
||||||
|
return rows
|
||||||
|
},
|
||||||
|
"FormatTwoColumns": func(rows [][2]string) string {
|
||||||
|
buf := bytes.NewBuffer(nil)
|
||||||
|
formatTwoColumns(buf, indent, indent, width, rows)
|
||||||
|
return buf.String()
|
||||||
|
},
|
||||||
|
"FormatTwoColumnsWithIndent": func(rows [][2]string, indent, padding int) string {
|
||||||
|
buf := bytes.NewBuffer(nil)
|
||||||
|
formatTwoColumns(buf, indent, padding, width, rows)
|
||||||
|
return buf.String()
|
||||||
|
},
|
||||||
|
"FormatAppUsage": formatAppUsage,
|
||||||
|
"FormatCommandUsage": formatCmdUsage,
|
||||||
|
"IsCumulative": func(value Value) bool {
|
||||||
|
r, ok := value.(remainderArg)
|
||||||
|
return ok && r.IsCumulative()
|
||||||
|
},
|
||||||
|
"Char": func(c rune) string {
|
||||||
|
return string(c)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
t, err := template.New("usage").Funcs(funcs).Parse(tmpl)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var selectedCommand *CmdModel
|
||||||
|
if context.SelectedCommand != nil {
|
||||||
|
selectedCommand = context.SelectedCommand.Model()
|
||||||
|
}
|
||||||
|
ctx := templateContext{
|
||||||
|
App: a.Model(),
|
||||||
|
Width: width,
|
||||||
|
Context: &templateParseContext{
|
||||||
|
SelectedCommand: selectedCommand,
|
||||||
|
FlagGroupModel: context.flags.Model(),
|
||||||
|
ArgGroupModel: context.arguments.Model(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return t.Execute(a.usageWriter, ctx)
|
||||||
|
}
|
|
@ -0,0 +1,466 @@
|
||||||
|
package kingpin
|
||||||
|
|
||||||
|
//go:generate go run ./cmd/genvalues/main.go
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"reflect"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/alecthomas/units"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NOTE: Most of the base type values were lifted from:
|
||||||
|
// http://golang.org/src/pkg/flag/flag.go?s=20146:20222
|
||||||
|
|
||||||
|
// Value is the interface to the dynamic value stored in a flag.
|
||||||
|
// (The default value is represented as a string.)
|
||||||
|
//
|
||||||
|
// If a Value has an IsBoolFlag() bool method returning true, the command-line
|
||||||
|
// parser makes --name equivalent to -name=true rather than using the next
|
||||||
|
// command-line argument, and adds a --no-name counterpart for negating the
|
||||||
|
// flag.
|
||||||
|
type Value interface {
|
||||||
|
String() string
|
||||||
|
Set(string) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Getter is an interface that allows the contents of a Value to be retrieved.
|
||||||
|
// It wraps the Value interface, rather than being part of it, because it
|
||||||
|
// appeared after Go 1 and its compatibility rules. All Value types provided
|
||||||
|
// by this package satisfy the Getter interface.
|
||||||
|
type Getter interface {
|
||||||
|
Value
|
||||||
|
Get() interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Optional interface to indicate boolean flags that don't accept a value, and
|
||||||
|
// implicitly have a --no-<x> negation counterpart.
|
||||||
|
type boolFlag interface {
|
||||||
|
Value
|
||||||
|
IsBoolFlag() bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Optional interface for arguments that cumulatively consume all remaining
|
||||||
|
// input.
|
||||||
|
type remainderArg interface {
|
||||||
|
Value
|
||||||
|
IsCumulative() bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Optional interface for flags that can be repeated.
|
||||||
|
type repeatableFlag interface {
|
||||||
|
Value
|
||||||
|
IsCumulative() bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type accumulator struct {
|
||||||
|
element func(value interface{}) Value
|
||||||
|
typ reflect.Type
|
||||||
|
slice reflect.Value
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use reflection to accumulate values into a slice.
|
||||||
|
//
|
||||||
|
// target := []string{}
|
||||||
|
// newAccumulator(&target, func (value interface{}) Value {
|
||||||
|
// return newStringValue(value.(*string))
|
||||||
|
// })
|
||||||
|
func newAccumulator(slice interface{}, element func(value interface{}) Value) *accumulator {
|
||||||
|
typ := reflect.TypeOf(slice)
|
||||||
|
if typ.Kind() != reflect.Ptr || typ.Elem().Kind() != reflect.Slice {
|
||||||
|
panic("expected a pointer to a slice")
|
||||||
|
}
|
||||||
|
return &accumulator{
|
||||||
|
element: element,
|
||||||
|
typ: typ.Elem().Elem(),
|
||||||
|
slice: reflect.ValueOf(slice),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *accumulator) String() string {
|
||||||
|
out := []string{}
|
||||||
|
s := a.slice.Elem()
|
||||||
|
for i := 0; i < s.Len(); i++ {
|
||||||
|
out = append(out, a.element(s.Index(i).Addr().Interface()).String())
|
||||||
|
}
|
||||||
|
return strings.Join(out, ",")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *accumulator) Set(value string) error {
|
||||||
|
e := reflect.New(a.typ)
|
||||||
|
if err := a.element(e.Interface()).Set(value); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
slice := reflect.Append(a.slice.Elem(), e.Elem())
|
||||||
|
a.slice.Elem().Set(slice)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *accumulator) Get() interface{} {
|
||||||
|
return a.slice.Interface()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *accumulator) IsCumulative() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *boolValue) IsBoolFlag() bool { return true }
|
||||||
|
|
||||||
|
// -- time.Duration Value
|
||||||
|
type durationValue time.Duration
|
||||||
|
|
||||||
|
func newDurationValue(p *time.Duration) *durationValue {
|
||||||
|
return (*durationValue)(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *durationValue) Set(s string) error {
|
||||||
|
v, err := time.ParseDuration(s)
|
||||||
|
*d = durationValue(v)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *durationValue) Get() interface{} { return time.Duration(*d) }
|
||||||
|
|
||||||
|
func (d *durationValue) String() string { return (*time.Duration)(d).String() }
|
||||||
|
|
||||||
|
// -- map[string]string Value
|
||||||
|
type stringMapValue map[string]string
|
||||||
|
|
||||||
|
func newStringMapValue(p *map[string]string) *stringMapValue {
|
||||||
|
return (*stringMapValue)(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
var stringMapRegex = regexp.MustCompile("[:=]")
|
||||||
|
|
||||||
|
func (s *stringMapValue) Set(value string) error {
|
||||||
|
parts := stringMapRegex.Split(value, 2)
|
||||||
|
if len(parts) != 2 {
|
||||||
|
return fmt.Errorf("expected KEY=VALUE got '%s'", value)
|
||||||
|
}
|
||||||
|
(*s)[parts[0]] = parts[1]
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *stringMapValue) Get() interface{} {
|
||||||
|
return (map[string]string)(*s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *stringMapValue) String() string {
|
||||||
|
return fmt.Sprintf("%s", map[string]string(*s))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *stringMapValue) IsCumulative() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- net.IP Value
|
||||||
|
type ipValue net.IP
|
||||||
|
|
||||||
|
func newIPValue(p *net.IP) *ipValue {
|
||||||
|
return (*ipValue)(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *ipValue) Set(value string) error {
|
||||||
|
if ip := net.ParseIP(value); ip == nil {
|
||||||
|
return fmt.Errorf("'%s' is not an IP address", value)
|
||||||
|
} else {
|
||||||
|
*i = *(*ipValue)(&ip)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *ipValue) Get() interface{} {
|
||||||
|
return (net.IP)(*i)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *ipValue) String() string {
|
||||||
|
return (*net.IP)(i).String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- *net.TCPAddr Value
|
||||||
|
type tcpAddrValue struct {
|
||||||
|
addr **net.TCPAddr
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTCPAddrValue(p **net.TCPAddr) *tcpAddrValue {
|
||||||
|
return &tcpAddrValue{p}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *tcpAddrValue) Set(value string) error {
|
||||||
|
if addr, err := net.ResolveTCPAddr("tcp", value); err != nil {
|
||||||
|
return fmt.Errorf("'%s' is not a valid TCP address: %s", value, err)
|
||||||
|
} else {
|
||||||
|
*i.addr = addr
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tcpAddrValue) Get() interface{} {
|
||||||
|
return (*net.TCPAddr)(*t.addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *tcpAddrValue) String() string {
|
||||||
|
return (*i.addr).String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- existingFile Value
|
||||||
|
|
||||||
|
type fileStatValue struct {
|
||||||
|
path *string
|
||||||
|
predicate func(os.FileInfo) error
|
||||||
|
}
|
||||||
|
|
||||||
|
func newFileStatValue(p *string, predicate func(os.FileInfo) error) *fileStatValue {
|
||||||
|
return &fileStatValue{
|
||||||
|
path: p,
|
||||||
|
predicate: predicate,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *fileStatValue) Set(value string) error {
|
||||||
|
if s, err := os.Stat(value); os.IsNotExist(err) {
|
||||||
|
return fmt.Errorf("path '%s' does not exist", value)
|
||||||
|
} else if err != nil {
|
||||||
|
return err
|
||||||
|
} else if err := e.predicate(s); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*e.path = value
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *fileStatValue) Get() interface{} {
|
||||||
|
return (string)(*f.path)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *fileStatValue) String() string {
|
||||||
|
return *e.path
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- os.File value
|
||||||
|
|
||||||
|
type fileValue struct {
|
||||||
|
f **os.File
|
||||||
|
flag int
|
||||||
|
perm os.FileMode
|
||||||
|
}
|
||||||
|
|
||||||
|
func newFileValue(p **os.File, flag int, perm os.FileMode) *fileValue {
|
||||||
|
return &fileValue{p, flag, perm}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *fileValue) Set(value string) error {
|
||||||
|
if fd, err := os.OpenFile(value, f.flag, f.perm); err != nil {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
*f.f = fd
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *fileValue) Get() interface{} {
|
||||||
|
return (*os.File)(*f.f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *fileValue) String() string {
|
||||||
|
if *f.f == nil {
|
||||||
|
return "<nil>"
|
||||||
|
}
|
||||||
|
return (*f.f).Name()
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- url.URL Value
|
||||||
|
type urlValue struct {
|
||||||
|
u **url.URL
|
||||||
|
}
|
||||||
|
|
||||||
|
func newURLValue(p **url.URL) *urlValue {
|
||||||
|
return &urlValue{p}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *urlValue) Set(value string) error {
|
||||||
|
if url, err := url.Parse(value); err != nil {
|
||||||
|
return fmt.Errorf("invalid URL: %s", err)
|
||||||
|
} else {
|
||||||
|
*u.u = url
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *urlValue) Get() interface{} {
|
||||||
|
return (*url.URL)(*u.u)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *urlValue) String() string {
|
||||||
|
if *u.u == nil {
|
||||||
|
return "<nil>"
|
||||||
|
}
|
||||||
|
return (*u.u).String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- []*url.URL Value
|
||||||
|
type urlListValue []*url.URL
|
||||||
|
|
||||||
|
func newURLListValue(p *[]*url.URL) *urlListValue {
|
||||||
|
return (*urlListValue)(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *urlListValue) Set(value string) error {
|
||||||
|
if url, err := url.Parse(value); err != nil {
|
||||||
|
return fmt.Errorf("invalid URL: %s", err)
|
||||||
|
} else {
|
||||||
|
*u = append(*u, url)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *urlListValue) Get() interface{} {
|
||||||
|
return ([]*url.URL)(*u)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *urlListValue) String() string {
|
||||||
|
out := []string{}
|
||||||
|
for _, url := range *u {
|
||||||
|
out = append(out, url.String())
|
||||||
|
}
|
||||||
|
return strings.Join(out, ",")
|
||||||
|
}
|
||||||
|
|
||||||
|
// A flag whose value must be in a set of options.
|
||||||
|
type enumValue struct {
|
||||||
|
value *string
|
||||||
|
options []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func newEnumFlag(target *string, options ...string) *enumValue {
|
||||||
|
return &enumValue{
|
||||||
|
value: target,
|
||||||
|
options: options,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *enumValue) String() string {
|
||||||
|
return *a.value
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *enumValue) Set(value string) error {
|
||||||
|
for _, v := range a.options {
|
||||||
|
if v == value {
|
||||||
|
*a.value = value
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fmt.Errorf("enum value must be one of %s, got '%s'", strings.Join(a.options, ","), value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *enumValue) Get() interface{} {
|
||||||
|
return (string)(*e.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- []string Enum Value
|
||||||
|
type enumsValue struct {
|
||||||
|
value *[]string
|
||||||
|
options []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func newEnumsFlag(target *[]string, options ...string) *enumsValue {
|
||||||
|
return &enumsValue{
|
||||||
|
value: target,
|
||||||
|
options: options,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *enumsValue) Set(value string) error {
|
||||||
|
for _, v := range s.options {
|
||||||
|
if v == value {
|
||||||
|
*s.value = append(*s.value, value)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fmt.Errorf("enum value must be one of %s, got '%s'", strings.Join(s.options, ","), value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *enumsValue) Get() interface{} {
|
||||||
|
return ([]string)(*e.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *enumsValue) String() string {
|
||||||
|
return strings.Join(*s.value, ",")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *enumsValue) IsCumulative() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- units.Base2Bytes Value
|
||||||
|
type bytesValue units.Base2Bytes
|
||||||
|
|
||||||
|
func newBytesValue(p *units.Base2Bytes) *bytesValue {
|
||||||
|
return (*bytesValue)(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *bytesValue) Set(s string) error {
|
||||||
|
v, err := units.ParseBase2Bytes(s)
|
||||||
|
*d = bytesValue(v)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *bytesValue) Get() interface{} { return units.Base2Bytes(*d) }
|
||||||
|
|
||||||
|
func (d *bytesValue) String() string { return (*units.Base2Bytes)(d).String() }
|
||||||
|
|
||||||
|
func newExistingFileValue(target *string) *fileStatValue {
|
||||||
|
return newFileStatValue(target, func(s os.FileInfo) error {
|
||||||
|
if s.IsDir() {
|
||||||
|
return fmt.Errorf("'%s' is a directory", s.Name())
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func newExistingDirValue(target *string) *fileStatValue {
|
||||||
|
return newFileStatValue(target, func(s os.FileInfo) error {
|
||||||
|
if !s.IsDir() {
|
||||||
|
return fmt.Errorf("'%s' is a file", s.Name())
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func newExistingFileOrDirValue(target *string) *fileStatValue {
|
||||||
|
return newFileStatValue(target, func(s os.FileInfo) error { return nil })
|
||||||
|
}
|
||||||
|
|
||||||
|
type counterValue int
|
||||||
|
|
||||||
|
func newCounterValue(n *int) *counterValue {
|
||||||
|
return (*counterValue)(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *counterValue) Set(s string) error {
|
||||||
|
*c++
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *counterValue) Get() interface{} { return (int)(*c) }
|
||||||
|
func (c *counterValue) IsBoolFlag() bool { return true }
|
||||||
|
func (c *counterValue) String() string { return fmt.Sprintf("%d", *c) }
|
||||||
|
func (c *counterValue) IsCumulative() bool { return true }
|
||||||
|
|
||||||
|
func resolveHost(value string) (net.IP, error) {
|
||||||
|
if ip := net.ParseIP(value); ip != nil {
|
||||||
|
return ip, nil
|
||||||
|
} else {
|
||||||
|
if addr, err := net.ResolveIPAddr("ip", value); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else {
|
||||||
|
return addr.IP, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
[
|
||||||
|
{"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."}
|
||||||
|
]
|
|
@ -0,0 +1,821 @@
|
||||||
|
package kingpin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// This file is autogenerated by "go generate .". Do not modify.
|
||||||
|
|
||||||
|
// -- bool Value
|
||||||
|
type boolValue struct{ v *bool }
|
||||||
|
|
||||||
|
func newBoolValue(p *bool) *boolValue {
|
||||||
|
return &boolValue{p}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *boolValue) Set(s string) error {
|
||||||
|
v, err := strconv.ParseBool(s)
|
||||||
|
if err == nil {
|
||||||
|
*f.v = (bool)(v)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *boolValue) Get() interface{} { return (bool)(*f.v) }
|
||||||
|
|
||||||
|
func (f *boolValue) String() string { return fmt.Sprintf("%v", *f) }
|
||||||
|
|
||||||
|
// Bool parses the next command-line value as bool.
|
||||||
|
func (p *parserMixin) Bool() (target *bool) {
|
||||||
|
target = new(bool)
|
||||||
|
p.BoolVar(target)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parserMixin) BoolVar(target *bool) {
|
||||||
|
p.SetValue(newBoolValue(target))
|
||||||
|
}
|
||||||
|
|
||||||
|
// BoolList accumulates bool values into a slice.
|
||||||
|
func (p *parserMixin) BoolList() (target *[]bool) {
|
||||||
|
target = new([]bool)
|
||||||
|
p.BoolListVar(target)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parserMixin) BoolListVar(target *[]bool) {
|
||||||
|
p.SetValue(newAccumulator(target, func(v interface{}) Value {
|
||||||
|
return newBoolValue(v.(*bool))
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- string Value
|
||||||
|
type stringValue struct{ v *string }
|
||||||
|
|
||||||
|
func newStringValue(p *string) *stringValue {
|
||||||
|
return &stringValue{p}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *stringValue) Set(s string) error {
|
||||||
|
v, err := s, error(nil)
|
||||||
|
if err == nil {
|
||||||
|
*f.v = (string)(v)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *stringValue) Get() interface{} { return (string)(*f.v) }
|
||||||
|
|
||||||
|
func (f *stringValue) String() string { return string(*f.v) }
|
||||||
|
|
||||||
|
// String parses the next command-line value as string.
|
||||||
|
func (p *parserMixin) String() (target *string) {
|
||||||
|
target = new(string)
|
||||||
|
p.StringVar(target)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parserMixin) StringVar(target *string) {
|
||||||
|
p.SetValue(newStringValue(target))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Strings accumulates string values into a slice.
|
||||||
|
func (p *parserMixin) Strings() (target *[]string) {
|
||||||
|
target = new([]string)
|
||||||
|
p.StringsVar(target)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parserMixin) StringsVar(target *[]string) {
|
||||||
|
p.SetValue(newAccumulator(target, func(v interface{}) Value {
|
||||||
|
return newStringValue(v.(*string))
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- uint Value
|
||||||
|
type uintValue struct{ v *uint }
|
||||||
|
|
||||||
|
func newUintValue(p *uint) *uintValue {
|
||||||
|
return &uintValue{p}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *uintValue) Set(s string) error {
|
||||||
|
v, err := strconv.ParseUint(s, 0, 64)
|
||||||
|
if err == nil {
|
||||||
|
*f.v = (uint)(v)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *uintValue) Get() interface{} { return (uint)(*f.v) }
|
||||||
|
|
||||||
|
func (f *uintValue) String() string { return fmt.Sprintf("%v", *f) }
|
||||||
|
|
||||||
|
// Uint parses the next command-line value as uint.
|
||||||
|
func (p *parserMixin) Uint() (target *uint) {
|
||||||
|
target = new(uint)
|
||||||
|
p.UintVar(target)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parserMixin) UintVar(target *uint) {
|
||||||
|
p.SetValue(newUintValue(target))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uints accumulates uint values into a slice.
|
||||||
|
func (p *parserMixin) Uints() (target *[]uint) {
|
||||||
|
target = new([]uint)
|
||||||
|
p.UintsVar(target)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parserMixin) UintsVar(target *[]uint) {
|
||||||
|
p.SetValue(newAccumulator(target, func(v interface{}) Value {
|
||||||
|
return newUintValue(v.(*uint))
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- uint8 Value
|
||||||
|
type uint8Value struct{ v *uint8 }
|
||||||
|
|
||||||
|
func newUint8Value(p *uint8) *uint8Value {
|
||||||
|
return &uint8Value{p}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *uint8Value) Set(s string) error {
|
||||||
|
v, err := strconv.ParseUint(s, 0, 8)
|
||||||
|
if err == nil {
|
||||||
|
*f.v = (uint8)(v)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *uint8Value) Get() interface{} { return (uint8)(*f.v) }
|
||||||
|
|
||||||
|
func (f *uint8Value) String() string { return fmt.Sprintf("%v", *f) }
|
||||||
|
|
||||||
|
// Uint8 parses the next command-line value as uint8.
|
||||||
|
func (p *parserMixin) Uint8() (target *uint8) {
|
||||||
|
target = new(uint8)
|
||||||
|
p.Uint8Var(target)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parserMixin) Uint8Var(target *uint8) {
|
||||||
|
p.SetValue(newUint8Value(target))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uint8List accumulates uint8 values into a slice.
|
||||||
|
func (p *parserMixin) Uint8List() (target *[]uint8) {
|
||||||
|
target = new([]uint8)
|
||||||
|
p.Uint8ListVar(target)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parserMixin) Uint8ListVar(target *[]uint8) {
|
||||||
|
p.SetValue(newAccumulator(target, func(v interface{}) Value {
|
||||||
|
return newUint8Value(v.(*uint8))
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- uint16 Value
|
||||||
|
type uint16Value struct{ v *uint16 }
|
||||||
|
|
||||||
|
func newUint16Value(p *uint16) *uint16Value {
|
||||||
|
return &uint16Value{p}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *uint16Value) Set(s string) error {
|
||||||
|
v, err := strconv.ParseUint(s, 0, 16)
|
||||||
|
if err == nil {
|
||||||
|
*f.v = (uint16)(v)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *uint16Value) Get() interface{} { return (uint16)(*f.v) }
|
||||||
|
|
||||||
|
func (f *uint16Value) String() string { return fmt.Sprintf("%v", *f) }
|
||||||
|
|
||||||
|
// Uint16 parses the next command-line value as uint16.
|
||||||
|
func (p *parserMixin) Uint16() (target *uint16) {
|
||||||
|
target = new(uint16)
|
||||||
|
p.Uint16Var(target)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parserMixin) Uint16Var(target *uint16) {
|
||||||
|
p.SetValue(newUint16Value(target))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uint16List accumulates uint16 values into a slice.
|
||||||
|
func (p *parserMixin) Uint16List() (target *[]uint16) {
|
||||||
|
target = new([]uint16)
|
||||||
|
p.Uint16ListVar(target)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parserMixin) Uint16ListVar(target *[]uint16) {
|
||||||
|
p.SetValue(newAccumulator(target, func(v interface{}) Value {
|
||||||
|
return newUint16Value(v.(*uint16))
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- uint32 Value
|
||||||
|
type uint32Value struct{ v *uint32 }
|
||||||
|
|
||||||
|
func newUint32Value(p *uint32) *uint32Value {
|
||||||
|
return &uint32Value{p}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *uint32Value) Set(s string) error {
|
||||||
|
v, err := strconv.ParseUint(s, 0, 32)
|
||||||
|
if err == nil {
|
||||||
|
*f.v = (uint32)(v)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *uint32Value) Get() interface{} { return (uint32)(*f.v) }
|
||||||
|
|
||||||
|
func (f *uint32Value) String() string { return fmt.Sprintf("%v", *f) }
|
||||||
|
|
||||||
|
// Uint32 parses the next command-line value as uint32.
|
||||||
|
func (p *parserMixin) Uint32() (target *uint32) {
|
||||||
|
target = new(uint32)
|
||||||
|
p.Uint32Var(target)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parserMixin) Uint32Var(target *uint32) {
|
||||||
|
p.SetValue(newUint32Value(target))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uint32List accumulates uint32 values into a slice.
|
||||||
|
func (p *parserMixin) Uint32List() (target *[]uint32) {
|
||||||
|
target = new([]uint32)
|
||||||
|
p.Uint32ListVar(target)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parserMixin) Uint32ListVar(target *[]uint32) {
|
||||||
|
p.SetValue(newAccumulator(target, func(v interface{}) Value {
|
||||||
|
return newUint32Value(v.(*uint32))
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- uint64 Value
|
||||||
|
type uint64Value struct{ v *uint64 }
|
||||||
|
|
||||||
|
func newUint64Value(p *uint64) *uint64Value {
|
||||||
|
return &uint64Value{p}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *uint64Value) Set(s string) error {
|
||||||
|
v, err := strconv.ParseUint(s, 0, 64)
|
||||||
|
if err == nil {
|
||||||
|
*f.v = (uint64)(v)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *uint64Value) Get() interface{} { return (uint64)(*f.v) }
|
||||||
|
|
||||||
|
func (f *uint64Value) String() string { return fmt.Sprintf("%v", *f) }
|
||||||
|
|
||||||
|
// Uint64 parses the next command-line value as uint64.
|
||||||
|
func (p *parserMixin) Uint64() (target *uint64) {
|
||||||
|
target = new(uint64)
|
||||||
|
p.Uint64Var(target)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parserMixin) Uint64Var(target *uint64) {
|
||||||
|
p.SetValue(newUint64Value(target))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uint64List accumulates uint64 values into a slice.
|
||||||
|
func (p *parserMixin) Uint64List() (target *[]uint64) {
|
||||||
|
target = new([]uint64)
|
||||||
|
p.Uint64ListVar(target)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parserMixin) Uint64ListVar(target *[]uint64) {
|
||||||
|
p.SetValue(newAccumulator(target, func(v interface{}) Value {
|
||||||
|
return newUint64Value(v.(*uint64))
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- int Value
|
||||||
|
type intValue struct{ v *int }
|
||||||
|
|
||||||
|
func newIntValue(p *int) *intValue {
|
||||||
|
return &intValue{p}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *intValue) Set(s string) error {
|
||||||
|
v, err := strconv.ParseFloat(s, 64)
|
||||||
|
if err == nil {
|
||||||
|
*f.v = (int)(v)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *intValue) Get() interface{} { return (int)(*f.v) }
|
||||||
|
|
||||||
|
func (f *intValue) String() string { return fmt.Sprintf("%v", *f) }
|
||||||
|
|
||||||
|
// Int parses the next command-line value as int.
|
||||||
|
func (p *parserMixin) Int() (target *int) {
|
||||||
|
target = new(int)
|
||||||
|
p.IntVar(target)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parserMixin) IntVar(target *int) {
|
||||||
|
p.SetValue(newIntValue(target))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ints accumulates int values into a slice.
|
||||||
|
func (p *parserMixin) Ints() (target *[]int) {
|
||||||
|
target = new([]int)
|
||||||
|
p.IntsVar(target)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parserMixin) IntsVar(target *[]int) {
|
||||||
|
p.SetValue(newAccumulator(target, func(v interface{}) Value {
|
||||||
|
return newIntValue(v.(*int))
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- int8 Value
|
||||||
|
type int8Value struct{ v *int8 }
|
||||||
|
|
||||||
|
func newInt8Value(p *int8) *int8Value {
|
||||||
|
return &int8Value{p}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *int8Value) Set(s string) error {
|
||||||
|
v, err := strconv.ParseInt(s, 0, 8)
|
||||||
|
if err == nil {
|
||||||
|
*f.v = (int8)(v)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *int8Value) Get() interface{} { return (int8)(*f.v) }
|
||||||
|
|
||||||
|
func (f *int8Value) String() string { return fmt.Sprintf("%v", *f) }
|
||||||
|
|
||||||
|
// Int8 parses the next command-line value as int8.
|
||||||
|
func (p *parserMixin) Int8() (target *int8) {
|
||||||
|
target = new(int8)
|
||||||
|
p.Int8Var(target)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parserMixin) Int8Var(target *int8) {
|
||||||
|
p.SetValue(newInt8Value(target))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int8List accumulates int8 values into a slice.
|
||||||
|
func (p *parserMixin) Int8List() (target *[]int8) {
|
||||||
|
target = new([]int8)
|
||||||
|
p.Int8ListVar(target)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parserMixin) Int8ListVar(target *[]int8) {
|
||||||
|
p.SetValue(newAccumulator(target, func(v interface{}) Value {
|
||||||
|
return newInt8Value(v.(*int8))
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- int16 Value
|
||||||
|
type int16Value struct{ v *int16 }
|
||||||
|
|
||||||
|
func newInt16Value(p *int16) *int16Value {
|
||||||
|
return &int16Value{p}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *int16Value) Set(s string) error {
|
||||||
|
v, err := strconv.ParseInt(s, 0, 16)
|
||||||
|
if err == nil {
|
||||||
|
*f.v = (int16)(v)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *int16Value) Get() interface{} { return (int16)(*f.v) }
|
||||||
|
|
||||||
|
func (f *int16Value) String() string { return fmt.Sprintf("%v", *f) }
|
||||||
|
|
||||||
|
// Int16 parses the next command-line value as int16.
|
||||||
|
func (p *parserMixin) Int16() (target *int16) {
|
||||||
|
target = new(int16)
|
||||||
|
p.Int16Var(target)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parserMixin) Int16Var(target *int16) {
|
||||||
|
p.SetValue(newInt16Value(target))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int16List accumulates int16 values into a slice.
|
||||||
|
func (p *parserMixin) Int16List() (target *[]int16) {
|
||||||
|
target = new([]int16)
|
||||||
|
p.Int16ListVar(target)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parserMixin) Int16ListVar(target *[]int16) {
|
||||||
|
p.SetValue(newAccumulator(target, func(v interface{}) Value {
|
||||||
|
return newInt16Value(v.(*int16))
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- int32 Value
|
||||||
|
type int32Value struct{ v *int32 }
|
||||||
|
|
||||||
|
func newInt32Value(p *int32) *int32Value {
|
||||||
|
return &int32Value{p}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *int32Value) Set(s string) error {
|
||||||
|
v, err := strconv.ParseInt(s, 0, 32)
|
||||||
|
if err == nil {
|
||||||
|
*f.v = (int32)(v)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *int32Value) Get() interface{} { return (int32)(*f.v) }
|
||||||
|
|
||||||
|
func (f *int32Value) String() string { return fmt.Sprintf("%v", *f) }
|
||||||
|
|
||||||
|
// Int32 parses the next command-line value as int32.
|
||||||
|
func (p *parserMixin) Int32() (target *int32) {
|
||||||
|
target = new(int32)
|
||||||
|
p.Int32Var(target)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parserMixin) Int32Var(target *int32) {
|
||||||
|
p.SetValue(newInt32Value(target))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int32List accumulates int32 values into a slice.
|
||||||
|
func (p *parserMixin) Int32List() (target *[]int32) {
|
||||||
|
target = new([]int32)
|
||||||
|
p.Int32ListVar(target)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parserMixin) Int32ListVar(target *[]int32) {
|
||||||
|
p.SetValue(newAccumulator(target, func(v interface{}) Value {
|
||||||
|
return newInt32Value(v.(*int32))
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- int64 Value
|
||||||
|
type int64Value struct{ v *int64 }
|
||||||
|
|
||||||
|
func newInt64Value(p *int64) *int64Value {
|
||||||
|
return &int64Value{p}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *int64Value) Set(s string) error {
|
||||||
|
v, err := strconv.ParseInt(s, 0, 64)
|
||||||
|
if err == nil {
|
||||||
|
*f.v = (int64)(v)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *int64Value) Get() interface{} { return (int64)(*f.v) }
|
||||||
|
|
||||||
|
func (f *int64Value) String() string { return fmt.Sprintf("%v", *f) }
|
||||||
|
|
||||||
|
// Int64 parses the next command-line value as int64.
|
||||||
|
func (p *parserMixin) Int64() (target *int64) {
|
||||||
|
target = new(int64)
|
||||||
|
p.Int64Var(target)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parserMixin) Int64Var(target *int64) {
|
||||||
|
p.SetValue(newInt64Value(target))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int64List accumulates int64 values into a slice.
|
||||||
|
func (p *parserMixin) Int64List() (target *[]int64) {
|
||||||
|
target = new([]int64)
|
||||||
|
p.Int64ListVar(target)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parserMixin) Int64ListVar(target *[]int64) {
|
||||||
|
p.SetValue(newAccumulator(target, func(v interface{}) Value {
|
||||||
|
return newInt64Value(v.(*int64))
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- float64 Value
|
||||||
|
type float64Value struct{ v *float64 }
|
||||||
|
|
||||||
|
func newFloat64Value(p *float64) *float64Value {
|
||||||
|
return &float64Value{p}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *float64Value) Set(s string) error {
|
||||||
|
v, err := strconv.ParseFloat(s, 64)
|
||||||
|
if err == nil {
|
||||||
|
*f.v = (float64)(v)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *float64Value) Get() interface{} { return (float64)(*f.v) }
|
||||||
|
|
||||||
|
func (f *float64Value) String() string { return fmt.Sprintf("%v", *f) }
|
||||||
|
|
||||||
|
// Float64 parses the next command-line value as float64.
|
||||||
|
func (p *parserMixin) Float64() (target *float64) {
|
||||||
|
target = new(float64)
|
||||||
|
p.Float64Var(target)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parserMixin) Float64Var(target *float64) {
|
||||||
|
p.SetValue(newFloat64Value(target))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float64List accumulates float64 values into a slice.
|
||||||
|
func (p *parserMixin) Float64List() (target *[]float64) {
|
||||||
|
target = new([]float64)
|
||||||
|
p.Float64ListVar(target)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parserMixin) Float64ListVar(target *[]float64) {
|
||||||
|
p.SetValue(newAccumulator(target, func(v interface{}) Value {
|
||||||
|
return newFloat64Value(v.(*float64))
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- float32 Value
|
||||||
|
type float32Value struct{ v *float32 }
|
||||||
|
|
||||||
|
func newFloat32Value(p *float32) *float32Value {
|
||||||
|
return &float32Value{p}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *float32Value) Set(s string) error {
|
||||||
|
v, err := strconv.ParseFloat(s, 32)
|
||||||
|
if err == nil {
|
||||||
|
*f.v = (float32)(v)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *float32Value) Get() interface{} { return (float32)(*f.v) }
|
||||||
|
|
||||||
|
func (f *float32Value) String() string { return fmt.Sprintf("%v", *f) }
|
||||||
|
|
||||||
|
// Float32 parses the next command-line value as float32.
|
||||||
|
func (p *parserMixin) Float32() (target *float32) {
|
||||||
|
target = new(float32)
|
||||||
|
p.Float32Var(target)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parserMixin) Float32Var(target *float32) {
|
||||||
|
p.SetValue(newFloat32Value(target))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float32List accumulates float32 values into a slice.
|
||||||
|
func (p *parserMixin) Float32List() (target *[]float32) {
|
||||||
|
target = new([]float32)
|
||||||
|
p.Float32ListVar(target)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parserMixin) Float32ListVar(target *[]float32) {
|
||||||
|
p.SetValue(newAccumulator(target, func(v interface{}) Value {
|
||||||
|
return newFloat32Value(v.(*float32))
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
// DurationList accumulates time.Duration values into a slice.
|
||||||
|
func (p *parserMixin) DurationList() (target *[]time.Duration) {
|
||||||
|
target = new([]time.Duration)
|
||||||
|
p.DurationListVar(target)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parserMixin) DurationListVar(target *[]time.Duration) {
|
||||||
|
p.SetValue(newAccumulator(target, func(v interface{}) Value {
|
||||||
|
return newDurationValue(v.(*time.Duration))
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
// IPList accumulates net.IP values into a slice.
|
||||||
|
func (p *parserMixin) IPList() (target *[]net.IP) {
|
||||||
|
target = new([]net.IP)
|
||||||
|
p.IPListVar(target)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parserMixin) IPListVar(target *[]net.IP) {
|
||||||
|
p.SetValue(newAccumulator(target, func(v interface{}) Value {
|
||||||
|
return newIPValue(v.(*net.IP))
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
// TCPList accumulates *net.TCPAddr values into a slice.
|
||||||
|
func (p *parserMixin) TCPList() (target *[]*net.TCPAddr) {
|
||||||
|
target = new([]*net.TCPAddr)
|
||||||
|
p.TCPListVar(target)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parserMixin) TCPListVar(target *[]*net.TCPAddr) {
|
||||||
|
p.SetValue(newAccumulator(target, func(v interface{}) Value {
|
||||||
|
return newTCPAddrValue(v.(**net.TCPAddr))
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExistingFiles accumulates string values into a slice.
|
||||||
|
func (p *parserMixin) ExistingFiles() (target *[]string) {
|
||||||
|
target = new([]string)
|
||||||
|
p.ExistingFilesVar(target)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parserMixin) ExistingFilesVar(target *[]string) {
|
||||||
|
p.SetValue(newAccumulator(target, func(v interface{}) Value {
|
||||||
|
return newExistingFileValue(v.(*string))
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExistingDirs accumulates string values into a slice.
|
||||||
|
func (p *parserMixin) ExistingDirs() (target *[]string) {
|
||||||
|
target = new([]string)
|
||||||
|
p.ExistingDirsVar(target)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parserMixin) ExistingDirsVar(target *[]string) {
|
||||||
|
p.SetValue(newAccumulator(target, func(v interface{}) Value {
|
||||||
|
return newExistingDirValue(v.(*string))
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExistingFilesOrDirs accumulates string values into a slice.
|
||||||
|
func (p *parserMixin) ExistingFilesOrDirs() (target *[]string) {
|
||||||
|
target = new([]string)
|
||||||
|
p.ExistingFilesOrDirsVar(target)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parserMixin) ExistingFilesOrDirsVar(target *[]string) {
|
||||||
|
p.SetValue(newAccumulator(target, func(v interface{}) Value {
|
||||||
|
return newExistingFileOrDirValue(v.(*string))
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- *regexp.Regexp Value
|
||||||
|
type regexpValue struct{ v **regexp.Regexp }
|
||||||
|
|
||||||
|
func newRegexpValue(p **regexp.Regexp) *regexpValue {
|
||||||
|
return ®expValue{p}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *regexpValue) Set(s string) error {
|
||||||
|
v, err := regexp.Compile(s)
|
||||||
|
if err == nil {
|
||||||
|
*f.v = (*regexp.Regexp)(v)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *regexpValue) Get() interface{} { return (*regexp.Regexp)(*f.v) }
|
||||||
|
|
||||||
|
func (f *regexpValue) String() string { return fmt.Sprintf("%v", *f) }
|
||||||
|
|
||||||
|
// Regexp parses the next command-line value as *regexp.Regexp.
|
||||||
|
func (p *parserMixin) Regexp() (target **regexp.Regexp) {
|
||||||
|
target = new(*regexp.Regexp)
|
||||||
|
p.RegexpVar(target)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parserMixin) RegexpVar(target **regexp.Regexp) {
|
||||||
|
p.SetValue(newRegexpValue(target))
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegexpList accumulates *regexp.Regexp values into a slice.
|
||||||
|
func (p *parserMixin) RegexpList() (target *[]*regexp.Regexp) {
|
||||||
|
target = new([]*regexp.Regexp)
|
||||||
|
p.RegexpListVar(target)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parserMixin) RegexpListVar(target *[]*regexp.Regexp) {
|
||||||
|
p.SetValue(newAccumulator(target, func(v interface{}) Value {
|
||||||
|
return newRegexpValue(v.(**regexp.Regexp))
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- net.IP Value
|
||||||
|
type resolvedIPValue struct{ v *net.IP }
|
||||||
|
|
||||||
|
func newResolvedIPValue(p *net.IP) *resolvedIPValue {
|
||||||
|
return &resolvedIPValue{p}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *resolvedIPValue) Set(s string) error {
|
||||||
|
v, err := resolveHost(s)
|
||||||
|
if err == nil {
|
||||||
|
*f.v = (net.IP)(v)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *resolvedIPValue) Get() interface{} { return (net.IP)(*f.v) }
|
||||||
|
|
||||||
|
func (f *resolvedIPValue) String() string { return fmt.Sprintf("%v", *f) }
|
||||||
|
|
||||||
|
// Resolve a hostname or IP to an IP.
|
||||||
|
func (p *parserMixin) ResolvedIP() (target *net.IP) {
|
||||||
|
target = new(net.IP)
|
||||||
|
p.ResolvedIPVar(target)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parserMixin) ResolvedIPVar(target *net.IP) {
|
||||||
|
p.SetValue(newResolvedIPValue(target))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResolvedIPList accumulates net.IP values into a slice.
|
||||||
|
func (p *parserMixin) ResolvedIPList() (target *[]net.IP) {
|
||||||
|
target = new([]net.IP)
|
||||||
|
p.ResolvedIPListVar(target)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parserMixin) ResolvedIPListVar(target *[]net.IP) {
|
||||||
|
p.SetValue(newAccumulator(target, func(v interface{}) Value {
|
||||||
|
return newResolvedIPValue(v.(*net.IP))
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- []byte Value
|
||||||
|
type hexBytesValue struct{ v *[]byte }
|
||||||
|
|
||||||
|
func newHexBytesValue(p *[]byte) *hexBytesValue {
|
||||||
|
return &hexBytesValue{p}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *hexBytesValue) Set(s string) error {
|
||||||
|
v, err := hex.DecodeString(s)
|
||||||
|
if err == nil {
|
||||||
|
*f.v = ([]byte)(v)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *hexBytesValue) Get() interface{} { return ([]byte)(*f.v) }
|
||||||
|
|
||||||
|
func (f *hexBytesValue) String() string { return fmt.Sprintf("%v", *f) }
|
||||||
|
|
||||||
|
// Bytes as a hex string.
|
||||||
|
func (p *parserMixin) HexBytes() (target *[]byte) {
|
||||||
|
target = new([]byte)
|
||||||
|
p.HexBytesVar(target)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parserMixin) HexBytesVar(target *[]byte) {
|
||||||
|
p.SetValue(newHexBytesValue(target))
|
||||||
|
}
|
||||||
|
|
||||||
|
// HexBytesList accumulates []byte values into a slice.
|
||||||
|
func (p *parserMixin) HexBytesList() (target *[][]byte) {
|
||||||
|
target = new([][]byte)
|
||||||
|
p.HexBytesListVar(target)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parserMixin) HexBytesListVar(target *[][]byte) {
|
||||||
|
p.SetValue(newAccumulator(target, func(v interface{}) Value {
|
||||||
|
return newHexBytesValue(v.(*[]byte))
|
||||||
|
}))
|
||||||
|
}
|
|
@ -67,6 +67,12 @@
|
||||||
"path": "golang.org/x/crypto/blowfish",
|
"path": "golang.org/x/crypto/blowfish",
|
||||||
"revision": "f6b343c37ca80bfa8ea539da67a0b621f84fab1d",
|
"revision": "f6b343c37ca80bfa8ea539da67a0b621f84fab1d",
|
||||||
"revisionTime": "2016-12-21T04:54:10Z"
|
"revisionTime": "2016-12-21T04:54:10Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"checksumSHA1": "SeYI7DRWrd0Ku+CLavuwIz3EEmQ=",
|
||||||
|
"path": "gopkg.in/alecthomas/kingpin.v2",
|
||||||
|
"revision": "e9044be3ab2a8e11d4e1f418d12f0790d57e8d70",
|
||||||
|
"revisionTime": "2016-08-29T10:30:05Z"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"rootPath": "github.com/dannyvankooten/ana"
|
"rootPath": "github.com/dannyvankooten/ana"
|
||||||
|
|
Loading…
Reference in New Issue