mirror of https://github.com/k3d-io/k3d
parent
609be6c84c
commit
6e6aed3994
@ -0,0 +1,21 @@ |
|||||||
|
The MIT License (MIT) |
||||||
|
|
||||||
|
Copyright (c) 2013 Mitchell Hashimoto |
||||||
|
|
||||||
|
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,14 @@ |
|||||||
|
# go-homedir |
||||||
|
|
||||||
|
This is a Go library for detecting the user's home directory without |
||||||
|
the use of cgo, so the library can be used in cross-compilation environments. |
||||||
|
|
||||||
|
Usage is incredibly simple, just call `homedir.Dir()` to get the home directory |
||||||
|
for a user, and `homedir.Expand()` to expand the `~` in a path to the home |
||||||
|
directory. |
||||||
|
|
||||||
|
**Why not just use `os/user`?** The built-in `os/user` package requires |
||||||
|
cgo on Darwin systems. This means that any Go code that uses that package |
||||||
|
cannot cross compile. But 99% of the time the use for `os/user` is just to |
||||||
|
retrieve the home directory, which we can do for the current user without |
||||||
|
cgo. This library does that, enabling cross-compilation. |
@ -0,0 +1 @@ |
|||||||
|
module github.com/mitchellh/go-homedir |
@ -0,0 +1,167 @@ |
|||||||
|
package homedir |
||||||
|
|
||||||
|
import ( |
||||||
|
"bytes" |
||||||
|
"errors" |
||||||
|
"os" |
||||||
|
"os/exec" |
||||||
|
"path/filepath" |
||||||
|
"runtime" |
||||||
|
"strconv" |
||||||
|
"strings" |
||||||
|
"sync" |
||||||
|
) |
||||||
|
|
||||||
|
// DisableCache will disable caching of the home directory. Caching is enabled
|
||||||
|
// by default.
|
||||||
|
var DisableCache bool |
||||||
|
|
||||||
|
var homedirCache string |
||||||
|
var cacheLock sync.RWMutex |
||||||
|
|
||||||
|
// Dir returns the home directory for the executing user.
|
||||||
|
//
|
||||||
|
// This uses an OS-specific method for discovering the home directory.
|
||||||
|
// An error is returned if a home directory cannot be detected.
|
||||||
|
func Dir() (string, error) { |
||||||
|
if !DisableCache { |
||||||
|
cacheLock.RLock() |
||||||
|
cached := homedirCache |
||||||
|
cacheLock.RUnlock() |
||||||
|
if cached != "" { |
||||||
|
return cached, nil |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
cacheLock.Lock() |
||||||
|
defer cacheLock.Unlock() |
||||||
|
|
||||||
|
var result string |
||||||
|
var err error |
||||||
|
if runtime.GOOS == "windows" { |
||||||
|
result, err = dirWindows() |
||||||
|
} else { |
||||||
|
// Unix-like system, so just assume Unix
|
||||||
|
result, err = dirUnix() |
||||||
|
} |
||||||
|
|
||||||
|
if err != nil { |
||||||
|
return "", err |
||||||
|
} |
||||||
|
homedirCache = result |
||||||
|
return result, nil |
||||||
|
} |
||||||
|
|
||||||
|
// Expand expands the path to include the home directory if the path
|
||||||
|
// is prefixed with `~`. If it isn't prefixed with `~`, the path is
|
||||||
|
// returned as-is.
|
||||||
|
func Expand(path string) (string, error) { |
||||||
|
if len(path) == 0 { |
||||||
|
return path, nil |
||||||
|
} |
||||||
|
|
||||||
|
if path[0] != '~' { |
||||||
|
return path, nil |
||||||
|
} |
||||||
|
|
||||||
|
if len(path) > 1 && path[1] != '/' && path[1] != '\\' { |
||||||
|
return "", errors.New("cannot expand user-specific home dir") |
||||||
|
} |
||||||
|
|
||||||
|
dir, err := Dir() |
||||||
|
if err != nil { |
||||||
|
return "", err |
||||||
|
} |
||||||
|
|
||||||
|
return filepath.Join(dir, path[1:]), nil |
||||||
|
} |
||||||
|
|
||||||
|
// Reset clears the cache, forcing the next call to Dir to re-detect
|
||||||
|
// the home directory. This generally never has to be called, but can be
|
||||||
|
// useful in tests if you're modifying the home directory via the HOME
|
||||||
|
// env var or something.
|
||||||
|
func Reset() { |
||||||
|
cacheLock.Lock() |
||||||
|
defer cacheLock.Unlock() |
||||||
|
homedirCache = "" |
||||||
|
} |
||||||
|
|
||||||
|
func dirUnix() (string, error) { |
||||||
|
homeEnv := "HOME" |
||||||
|
if runtime.GOOS == "plan9" { |
||||||
|
// On plan9, env vars are lowercase.
|
||||||
|
homeEnv = "home" |
||||||
|
} |
||||||
|
|
||||||
|
// First prefer the HOME environmental variable
|
||||||
|
if home := os.Getenv(homeEnv); home != "" { |
||||||
|
return home, nil |
||||||
|
} |
||||||
|
|
||||||
|
var stdout bytes.Buffer |
||||||
|
|
||||||
|
// If that fails, try OS specific commands
|
||||||
|
if runtime.GOOS == "darwin" { |
||||||
|
cmd := exec.Command("sh", "-c", `dscl -q . -read /Users/"$(whoami)" NFSHomeDirectory | sed 's/^[^ ]*: //'`) |
||||||
|
cmd.Stdout = &stdout |
||||||
|
if err := cmd.Run(); err == nil { |
||||||
|
result := strings.TrimSpace(stdout.String()) |
||||||
|
if result != "" { |
||||||
|
return result, nil |
||||||
|
} |
||||||
|
} |
||||||
|
} else { |
||||||
|
cmd := exec.Command("getent", "passwd", strconv.Itoa(os.Getuid())) |
||||||
|
cmd.Stdout = &stdout |
||||||
|
if err := cmd.Run(); err != nil { |
||||||
|
// If the error is ErrNotFound, we ignore it. Otherwise, return it.
|
||||||
|
if err != exec.ErrNotFound { |
||||||
|
return "", err |
||||||
|
} |
||||||
|
} else { |
||||||
|
if passwd := strings.TrimSpace(stdout.String()); passwd != "" { |
||||||
|
// username:password:uid:gid:gecos:home:shell
|
||||||
|
passwdParts := strings.SplitN(passwd, ":", 7) |
||||||
|
if len(passwdParts) > 5 { |
||||||
|
return passwdParts[5], nil |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// If all else fails, try the shell
|
||||||
|
stdout.Reset() |
||||||
|
cmd := exec.Command("sh", "-c", "cd && pwd") |
||||||
|
cmd.Stdout = &stdout |
||||||
|
if err := cmd.Run(); err != nil { |
||||||
|
return "", err |
||||||
|
} |
||||||
|
|
||||||
|
result := strings.TrimSpace(stdout.String()) |
||||||
|
if result == "" { |
||||||
|
return "", errors.New("blank output when reading home directory") |
||||||
|
} |
||||||
|
|
||||||
|
return result, nil |
||||||
|
} |
||||||
|
|
||||||
|
func dirWindows() (string, error) { |
||||||
|
// First prefer the HOME environmental variable
|
||||||
|
if home := os.Getenv("HOME"); home != "" { |
||||||
|
return home, nil |
||||||
|
} |
||||||
|
|
||||||
|
// Prefer standard environment variable USERPROFILE
|
||||||
|
if home := os.Getenv("USERPROFILE"); home != "" { |
||||||
|
return home, nil |
||||||
|
} |
||||||
|
|
||||||
|
drive := os.Getenv("HOMEDRIVE") |
||||||
|
path := os.Getenv("HOMEPATH") |
||||||
|
home := drive + path |
||||||
|
if drive == "" || path == "" { |
||||||
|
return "", errors.New("HOMEDRIVE, HOMEPATH, or USERPROFILE are blank") |
||||||
|
} |
||||||
|
|
||||||
|
return home, nil |
||||||
|
} |
@ -0,0 +1,2 @@ |
|||||||
|
[flake8] |
||||||
|
max-line-length = 120 |
@ -0,0 +1,2 @@ |
|||||||
|
*.coverprofile |
||||||
|
node_modules/ |
@ -0,0 +1,27 @@ |
|||||||
|
language: go |
||||||
|
sudo: false |
||||||
|
dist: trusty |
||||||
|
osx_image: xcode8.3 |
||||||
|
go: 1.8.x |
||||||
|
|
||||||
|
os: |
||||||
|
- linux |
||||||
|
- osx |
||||||
|
|
||||||
|
cache: |
||||||
|
directories: |
||||||
|
- node_modules |
||||||
|
|
||||||
|
before_script: |
||||||
|
- go get github.com/urfave/gfmrun/... || true |
||||||
|
- go get golang.org/x/tools/cmd/goimports |
||||||
|
- if [ ! -f node_modules/.bin/markdown-toc ] ; then |
||||||
|
npm install markdown-toc ; |
||||||
|
fi |
||||||
|
|
||||||
|
script: |
||||||
|
- ./runtests gen |
||||||
|
- ./runtests vet |
||||||
|
- ./runtests test |
||||||
|
- ./runtests gfmrun |
||||||
|
- ./runtests toc |
@ -0,0 +1,435 @@ |
|||||||
|
# Change Log |
||||||
|
|
||||||
|
**ATTN**: This project uses [semantic versioning](http://semver.org/). |
||||||
|
|
||||||
|
## [Unreleased] |
||||||
|
|
||||||
|
## 1.20.0 - 2017-08-10 |
||||||
|
|
||||||
|
### Fixed |
||||||
|
|
||||||
|
* `HandleExitCoder` is now correctly iterates over all errors in |
||||||
|
a `MultiError`. The exit code is the exit code of the last error or `1` if |
||||||
|
there are no `ExitCoder`s in the `MultiError`. |
||||||
|
* Fixed YAML file loading on Windows (previously would fail validate the file path) |
||||||
|
* Subcommand `Usage`, `Description`, `ArgsUsage`, `OnUsageError` correctly |
||||||
|
propogated |
||||||
|
* `ErrWriter` is now passed downwards through command structure to avoid the |
||||||
|
need to redefine it |
||||||
|
* Pass `Command` context into `OnUsageError` rather than parent context so that |
||||||
|
all fields are avaiable |
||||||
|
* Errors occuring in `Before` funcs are no longer double printed |
||||||
|
* Use `UsageText` in the help templates for commands and subcommands if |
||||||
|
defined; otherwise build the usage as before (was previously ignoring this |
||||||
|
field) |
||||||
|
* `IsSet` and `GlobalIsSet` now correctly return whether a flag is set if |
||||||
|
a program calls `Set` or `GlobalSet` directly after flag parsing (would |
||||||
|
previously only return `true` if the flag was set during parsing) |
||||||
|
|
||||||
|
### Changed |
||||||
|
|
||||||
|
* No longer exit the program on command/subcommand error if the error raised is |
||||||
|
not an `OsExiter`. This exiting behavior was introduced in 1.19.0, but was |
||||||
|
determined to be a regression in functionality. See [the |
||||||
|
PR](https://github.com/urfave/cli/pull/595) for discussion. |
||||||
|
|
||||||
|
### Added |
||||||
|
|
||||||
|
* `CommandsByName` type was added to make it easy to sort `Command`s by name, |
||||||
|
alphabetically |
||||||
|
* `altsrc` now handles loading of string and int arrays from TOML |
||||||
|
* Support for definition of custom help templates for `App` via |
||||||
|
`CustomAppHelpTemplate` |
||||||
|
* Support for arbitrary key/value fields on `App` to be used with |
||||||
|
`CustomAppHelpTemplate` via `ExtraInfo` |
||||||
|
* `HelpFlag`, `VersionFlag`, and `BashCompletionFlag` changed to explictly be |
||||||
|
`cli.Flag`s allowing for the use of custom flags satisfying the `cli.Flag` |
||||||
|
interface to be used. |
||||||
|
|
||||||
|
|
||||||
|
## [1.19.1] - 2016-11-21 |
||||||
|
|
||||||
|
### Fixed |
||||||
|
|
||||||
|
- Fixes regression introduced in 1.19.0 where using an `ActionFunc` as |
||||||
|
the `Action` for a command would cause it to error rather than calling the |
||||||
|
function. Should not have a affected declarative cases using `func(c |
||||||
|
*cli.Context) err)`. |
||||||
|
- Shell completion now handles the case where the user specifies |
||||||
|
`--generate-bash-completion` immediately after a flag that takes an argument. |
||||||
|
Previously it call the application with `--generate-bash-completion` as the |
||||||
|
flag value. |
||||||
|
|
||||||
|
## [1.19.0] - 2016-11-19 |
||||||
|
### Added |
||||||
|
- `FlagsByName` was added to make it easy to sort flags (e.g. `sort.Sort(cli.FlagsByName(app.Flags))`) |
||||||
|
- A `Description` field was added to `App` for a more detailed description of |
||||||
|
the application (similar to the existing `Description` field on `Command`) |
||||||
|
- Flag type code generation via `go generate` |
||||||
|
- Write to stderr and exit 1 if action returns non-nil error |
||||||
|
- Added support for TOML to the `altsrc` loader |
||||||
|
- `SkipArgReorder` was added to allow users to skip the argument reordering. |
||||||
|
This is useful if you want to consider all "flags" after an argument as |
||||||
|
arguments rather than flags (the default behavior of the stdlib `flag` |
||||||
|
library). This is backported functionality from the [removal of the flag |
||||||
|
reordering](https://github.com/urfave/cli/pull/398) in the unreleased version |
||||||
|
2 |
||||||
|
- For formatted errors (those implementing `ErrorFormatter`), the errors will |
||||||
|
be formatted during output. Compatible with `pkg/errors`. |
||||||
|
|
||||||
|
### Changed |
||||||
|
- Raise minimum tested/supported Go version to 1.2+ |
||||||
|
|
||||||
|
### Fixed |
||||||
|
- Consider empty environment variables as set (previously environment variables |
||||||
|
with the equivalent of `""` would be skipped rather than their value used). |
||||||
|
- Return an error if the value in a given environment variable cannot be parsed |
||||||
|
as the flag type. Previously these errors were silently swallowed. |
||||||
|
- Print full error when an invalid flag is specified (which includes the invalid flag) |
||||||
|
- `App.Writer` defaults to `stdout` when `nil` |
||||||
|
- If no action is specified on a command or app, the help is now printed instead of `panic`ing |
||||||
|
- `App.Metadata` is initialized automatically now (previously was `nil` unless initialized) |
||||||
|
- Correctly show help message if `-h` is provided to a subcommand |
||||||
|
- `context.(Global)IsSet` now respects environment variables. Previously it |
||||||
|
would return `false` if a flag was specified in the environment rather than |
||||||
|
as an argument |
||||||
|
- Removed deprecation warnings to STDERR to avoid them leaking to the end-user |
||||||
|
- `altsrc`s import paths were updated to use `gopkg.in/urfave/cli.v1`. This |
||||||
|
fixes issues that occurred when `gopkg.in/urfave/cli.v1` was imported as well |
||||||
|
as `altsrc` where Go would complain that the types didn't match |
||||||
|
|
||||||
|
## [1.18.1] - 2016-08-28 |
||||||
|
### Fixed |
||||||
|
- Removed deprecation warnings to STDERR to avoid them leaking to the end-user (backported) |
||||||
|
|
||||||
|
## [1.18.0] - 2016-06-27 |
||||||
|
### Added |
||||||
|
- `./runtests` test runner with coverage tracking by default |
||||||
|
- testing on OS X |
||||||
|
- testing on Windows |
||||||
|
- `UintFlag`, `Uint64Flag`, and `Int64Flag` types and supporting code |
||||||
|
|
||||||
|
### Changed |
||||||
|
- Use spaces for alignment in help/usage output instead of tabs, making the |
||||||
|
output alignment consistent regardless of tab width |
||||||
|
|
||||||
|
### Fixed |
||||||
|
- Printing of command aliases in help text |
||||||
|
- Printing of visible flags for both struct and struct pointer flags |
||||||
|
- Display the `help` subcommand when using `CommandCategories` |
||||||
|
- No longer swallows `panic`s that occur within the `Action`s themselves when |
||||||
|
detecting the signature of the `Action` field |
||||||
|
|
||||||
|
## [1.17.1] - 2016-08-28 |
||||||
|
### Fixed |
||||||
|
- Removed deprecation warnings to STDERR to avoid them leaking to the end-user |
||||||
|
|
||||||
|
## [1.17.0] - 2016-05-09 |
||||||
|
### Added |
||||||
|
- Pluggable flag-level help text rendering via `cli.DefaultFlagStringFunc` |
||||||
|
- `context.GlobalBoolT` was added as an analogue to `context.GlobalBool` |
||||||
|
- Support for hiding commands by setting `Hidden: true` -- this will hide the |
||||||
|
commands in help output |
||||||
|
|
||||||
|
### Changed |
||||||
|
- `Float64Flag`, `IntFlag`, and `DurationFlag` default values are no longer |
||||||
|
quoted in help text output. |
||||||
|
- All flag types now include `(default: {value})` strings following usage when a |
||||||
|
default value can be (reasonably) detected. |
||||||
|
- `IntSliceFlag` and `StringSliceFlag` usage strings are now more consistent |
||||||
|
with non-slice flag types |
||||||
|
- Apps now exit with a code of 3 if an unknown subcommand is specified |
||||||
|
(previously they printed "No help topic for...", but still exited 0. This |
||||||
|
makes it easier to script around apps built using `cli` since they can trust |
||||||
|
that a 0 exit code indicated a successful execution. |
||||||
|
- cleanups based on [Go Report Card |
||||||
|
feedback](https://goreportcard.com/report/github.com/urfave/cli) |
||||||
|
|
||||||
|
## [1.16.1] - 2016-08-28 |
||||||
|
### Fixed |
||||||
|
- Removed deprecation warnings to STDERR to avoid them leaking to the end-user |
||||||
|
|
||||||
|
## [1.16.0] - 2016-05-02 |
||||||
|
### Added |
||||||
|
- `Hidden` field on all flag struct types to omit from generated help text |
||||||
|
|
||||||
|
### Changed |
||||||
|
- `BashCompletionFlag` (`--enable-bash-completion`) is now omitted from |
||||||
|
generated help text via the `Hidden` field |
||||||
|
|
||||||
|
### Fixed |
||||||
|
- handling of error values in `HandleAction` and `HandleExitCoder` |
||||||
|
|
||||||
|
## [1.15.0] - 2016-04-30 |
||||||
|
### Added |
||||||
|
- This file! |
||||||
|
- Support for placeholders in flag usage strings |
||||||
|
- `App.Metadata` map for arbitrary data/state management |
||||||
|
- `Set` and `GlobalSet` methods on `*cli.Context` for altering values after |
||||||
|
parsing. |
||||||
|
- Support for nested lookup of dot-delimited keys in structures loaded from |
||||||
|
YAML. |
||||||
|
|
||||||
|
### Changed |
||||||
|
- The `App.Action` and `Command.Action` now prefer a return signature of |
||||||
|
`func(*cli.Context) error`, as defined by `cli.ActionFunc`. If a non-nil |
||||||
|
`error` is returned, there may be two outcomes: |
||||||
|
- If the error fulfills `cli.ExitCoder`, then `os.Exit` will be called |
||||||
|
automatically |
||||||
|
- Else the error is bubbled up and returned from `App.Run` |
||||||
|
- Specifying an `Action` with the legacy return signature of |
||||||
|
`func(*cli.Context)` will produce a deprecation message to stderr |
||||||
|
- Specifying an `Action` that is not a `func` type will produce a non-zero exit |
||||||
|
from `App.Run` |
||||||
|
- Specifying an `Action` func that has an invalid (input) signature will |
||||||
|
produce a non-zero exit from `App.Run` |
||||||
|
|
||||||
|
### Deprecated |
||||||
|
- <a name="deprecated-cli-app-runandexitonerror"></a> |
||||||
|
`cli.App.RunAndExitOnError`, which should now be done by returning an error |
||||||
|
that fulfills `cli.ExitCoder` to `cli.App.Run`. |
||||||
|
- <a name="deprecated-cli-app-action-signature"></a> the legacy signature for |
||||||
|
`cli.App.Action` of `func(*cli.Context)`, which should now have a return |
||||||
|
signature of `func(*cli.Context) error`, as defined by `cli.ActionFunc`. |
||||||
|
|
||||||
|
### Fixed |
||||||
|
- Added missing `*cli.Context.GlobalFloat64` method |
||||||
|
|
||||||
|
## [1.14.0] - 2016-04-03 (backfilled 2016-04-25) |
||||||
|
### Added |
||||||
|
- Codebeat badge |
||||||
|
- Support for categorization via `CategorizedHelp` and `Categories` on app. |
||||||
|
|
||||||
|
### Changed |
||||||
|
- Use `filepath.Base` instead of `path.Base` in `Name` and `HelpName`. |
||||||
|
|
||||||
|
### Fixed |
||||||
|
- Ensure version is not shown in help text when `HideVersion` set. |
||||||
|
|
||||||
|
## [1.13.0] - 2016-03-06 (backfilled 2016-04-25) |
||||||
|
### Added |
||||||
|
- YAML file input support. |
||||||
|
- `NArg` method on context. |
||||||
|
|
||||||
|
## [1.12.0] - 2016-02-17 (backfilled 2016-04-25) |
||||||
|
### Added |
||||||
|
- Custom usage error handling. |
||||||
|
- Custom text support in `USAGE` section of help output. |
||||||
|
- Improved help messages for empty strings. |
||||||
|
- AppVeyor CI configuration. |
||||||
|
|
||||||
|
### Changed |
||||||
|
- Removed `panic` from default help printer func. |
||||||
|
- De-duping and optimizations. |
||||||
|
|
||||||
|
### Fixed |
||||||
|
- Correctly handle `Before`/`After` at command level when no subcommands. |
||||||
|
- Case of literal `-` argument causing flag reordering. |
||||||
|
- Environment variable hints on Windows. |
||||||
|
- Docs updates. |
||||||
|
|
||||||
|
## [1.11.1] - 2015-12-21 (backfilled 2016-04-25) |
||||||
|
### Changed |
||||||
|
- Use `path.Base` in `Name` and `HelpName` |
||||||
|
- Export `GetName` on flag types. |
||||||
|
|
||||||
|
### Fixed |
||||||
|
- Flag parsing when skipping is enabled. |
||||||
|
- Test output cleanup. |
||||||
|
- Move completion check to account for empty input case. |
||||||
|
|
||||||
|
## [1.11.0] - 2015-11-15 (backfilled 2016-04-25) |
||||||
|
### Added |
||||||
|
- Destination scan support for flags. |
||||||
|
- Testing against `tip` in Travis CI config. |
||||||
|
|
||||||
|
### Changed |
||||||
|
- Go version in Travis CI config. |
||||||
|
|
||||||
|
### Fixed |
||||||
|
- Removed redundant tests. |
||||||
|
- Use correct example naming in tests. |
||||||
|
|
||||||
|
## [1.10.2] - 2015-10-29 (backfilled 2016-04-25) |
||||||
|
### Fixed |
||||||
|
- Remove unused var in bash completion. |
||||||
|
|
||||||
|
## [1.10.1] - 2015-10-21 (backfilled 2016-04-25) |
||||||
|
### Added |
||||||
|
- Coverage and reference logos in README. |
||||||
|
|
||||||
|
### Fixed |
||||||
|
- Use specified values in help and version parsing. |
||||||
|
- Only display app version and help message once. |
||||||
|
|
||||||
|
## [1.10.0] - 2015-10-06 (backfilled 2016-04-25) |
||||||
|
### Added |
||||||
|
- More tests for existing functionality. |
||||||
|
- `ArgsUsage` at app and command level for help text flexibility. |
||||||
|
|
||||||
|
### Fixed |
||||||
|
- Honor `HideHelp` and `HideVersion` in `App.Run`. |
||||||
|
- Remove juvenile word from README. |
||||||
|
|
||||||
|
## [1.9.0] - 2015-09-08 (backfilled 2016-04-25) |
||||||
|
### Added |
||||||
|
- `FullName` on command with accompanying help output update. |
||||||
|
- Set default `$PROG` in bash completion. |
||||||
|
|
||||||
|
### Changed |
||||||
|
- Docs formatting. |
||||||
|
|
||||||
|
### Fixed |
||||||
|
- Removed self-referential imports in tests. |
||||||
|
|
||||||
|
## [1.8.0] - 2015-06-30 (backfilled 2016-04-25) |
||||||
|
### Added |
||||||
|
- Support for `Copyright` at app level. |
||||||
|
- `Parent` func at context level to walk up context lineage. |
||||||
|
|
||||||
|
### Fixed |
||||||
|
- Global flag processing at top level. |
||||||
|
|
||||||
|
## [1.7.1] - 2015-06-11 (backfilled 2016-04-25) |
||||||
|
### Added |
||||||
|
- Aggregate errors from `Before`/`After` funcs. |
||||||
|
- Doc comments on flag structs. |
||||||
|
- Include non-global flags when checking version and help. |
||||||
|
- Travis CI config updates. |
||||||
|
|
||||||
|
### Fixed |
||||||
|
- Ensure slice type flags have non-nil values. |
||||||
|
- Collect global flags from the full command hierarchy. |
||||||
|
- Docs prose. |
||||||
|
|
||||||
|
## [1.7.0] - 2015-05-03 (backfilled 2016-04-25) |
||||||
|
### Changed |
||||||
|
- `HelpPrinter` signature includes output writer. |
||||||
|
|
||||||
|
### Fixed |
||||||
|
- Specify go 1.1+ in docs. |
||||||
|
- Set `Writer` when running command as app. |
||||||
|
|
||||||
|
## [1.6.0] - 2015-03-23 (backfilled 2016-04-25) |
||||||
|
### Added |
||||||
|
- Multiple author support. |
||||||
|
- `NumFlags` at context level. |
||||||
|
- `Aliases` at command level. |
||||||
|
|
||||||
|
### Deprecated |
||||||
|
- `ShortName` at command level. |
||||||
|
|
||||||
|
### Fixed |
||||||
|
- Subcommand help output. |
||||||
|
- Backward compatible support for deprecated `Author` and `Email` fields. |
||||||
|
- Docs regarding `Names`/`Aliases`. |
||||||
|
|
||||||
|
## [1.5.0] - 2015-02-20 (backfilled 2016-04-25) |
||||||
|
### Added |
||||||
|
- `After` hook func support at app and command level. |
||||||
|
|
||||||
|
### Fixed |
||||||
|
- Use parsed context when running command as subcommand. |
||||||
|
- Docs prose. |
||||||
|
|
||||||
|
## [1.4.1] - 2015-01-09 (backfilled 2016-04-25) |
||||||
|
### Added |
||||||
|
- Support for hiding `-h / --help` flags, but not `help` subcommand. |
||||||
|
- Stop flag parsing after `--`. |
||||||
|
|
||||||
|
### Fixed |
||||||
|
- Help text for generic flags to specify single value. |
||||||
|
- Use double quotes in output for defaults. |
||||||
|
- Use `ParseInt` instead of `ParseUint` for int environment var values. |
||||||
|
- Use `0` as base when parsing int environment var values. |
||||||
|
|
||||||
|
## [1.4.0] - 2014-12-12 (backfilled 2016-04-25) |
||||||
|
### Added |
||||||
|
- Support for environment variable lookup "cascade". |
||||||
|
- Support for `Stdout` on app for output redirection. |
||||||
|
|
||||||
|
### Fixed |
||||||
|
- Print command help instead of app help in `ShowCommandHelp`. |
||||||
|
|
||||||
|
## [1.3.1] - 2014-11-13 (backfilled 2016-04-25) |
||||||
|
### Added |
||||||
|
- Docs and example code updates. |
||||||
|
|
||||||
|
### Changed |
||||||
|
- Default `-v / --version` flag made optional. |
||||||
|
|
||||||
|
## [1.3.0] - 2014-08-10 (backfilled 2016-04-25) |
||||||
|
### Added |
||||||
|
- `FlagNames` at context level. |
||||||
|
- Exposed `VersionPrinter` var for more control over version output. |
||||||
|
- Zsh completion hook. |
||||||
|
- `AUTHOR` section in default app help template. |
||||||
|
- Contribution guidelines. |
||||||
|
- `DurationFlag` type. |
||||||
|
|
||||||
|
## [1.2.0] - 2014-08-02 |
||||||
|
### Added |
||||||
|
- Support for environment variable defaults on flags plus tests. |
||||||
|
|
||||||
|
## [1.1.0] - 2014-07-15 |
||||||
|
### Added |
||||||
|
- Bash completion. |
||||||
|
- Optional hiding of built-in help command. |
||||||
|
- Optional skipping of flag parsing at command level. |
||||||
|
- `Author`, `Email`, and `Compiled` metadata on app. |
||||||
|
- `Before` hook func support at app and command level. |
||||||
|
- `CommandNotFound` func support at app level. |
||||||
|
- Command reference available on context. |
||||||
|
- `GenericFlag` type. |
||||||
|
- `Float64Flag` type. |
||||||
|
- `BoolTFlag` type. |
||||||
|
- `IsSet` flag helper on context. |
||||||
|
- More flag lookup funcs at context level. |
||||||
|
- More tests & docs. |
||||||
|
|
||||||
|
### Changed |
||||||
|
- Help template updates to account for presence/absence of flags. |
||||||
|
- Separated subcommand help template. |
||||||
|
- Exposed `HelpPrinter` var for more control over help output. |
||||||
|
|
||||||
|
## [1.0.0] - 2013-11-01 |
||||||
|
### Added |
||||||
|
- `help` flag in default app flag set and each command flag set. |
||||||
|
- Custom handling of argument parsing errors. |
||||||
|
- Command lookup by name at app level. |
||||||
|
- `StringSliceFlag` type and supporting `StringSlice` type. |
||||||
|
- `IntSliceFlag` type and supporting `IntSlice` type. |
||||||
|
- Slice type flag lookups by name at context level. |
||||||
|
- Export of app and command help functions. |
||||||
|
- More tests & docs. |
||||||
|
|
||||||
|
## 0.1.0 - 2013-07-22 |
||||||
|
### Added |
||||||
|
- Initial implementation. |
||||||
|
|
||||||
|
[Unreleased]: https://github.com/urfave/cli/compare/v1.18.0...HEAD |
||||||
|
[1.18.0]: https://github.com/urfave/cli/compare/v1.17.0...v1.18.0 |
||||||
|
[1.17.0]: https://github.com/urfave/cli/compare/v1.16.0...v1.17.0 |
||||||
|
[1.16.0]: https://github.com/urfave/cli/compare/v1.15.0...v1.16.0 |
||||||
|
[1.15.0]: https://github.com/urfave/cli/compare/v1.14.0...v1.15.0 |
||||||
|
[1.14.0]: https://github.com/urfave/cli/compare/v1.13.0...v1.14.0 |
||||||
|
[1.13.0]: https://github.com/urfave/cli/compare/v1.12.0...v1.13.0 |
||||||
|
[1.12.0]: https://github.com/urfave/cli/compare/v1.11.1...v1.12.0 |
||||||
|
[1.11.1]: https://github.com/urfave/cli/compare/v1.11.0...v1.11.1 |
||||||
|
[1.11.0]: https://github.com/urfave/cli/compare/v1.10.2...v1.11.0 |
||||||
|
[1.10.2]: https://github.com/urfave/cli/compare/v1.10.1...v1.10.2 |
||||||
|
[1.10.1]: https://github.com/urfave/cli/compare/v1.10.0...v1.10.1 |
||||||
|
[1.10.0]: https://github.com/urfave/cli/compare/v1.9.0...v1.10.0 |
||||||
|
[1.9.0]: https://github.com/urfave/cli/compare/v1.8.0...v1.9.0 |
||||||
|
[1.8.0]: https://github.com/urfave/cli/compare/v1.7.1...v1.8.0 |
||||||
|
[1.7.1]: https://github.com/urfave/cli/compare/v1.7.0...v1.7.1 |
||||||
|
[1.7.0]: https://github.com/urfave/cli/compare/v1.6.0...v1.7.0 |
||||||
|
[1.6.0]: https://github.com/urfave/cli/compare/v1.5.0...v1.6.0 |
||||||
|
[1.5.0]: https://github.com/urfave/cli/compare/v1.4.1...v1.5.0 |
||||||
|
[1.4.1]: https://github.com/urfave/cli/compare/v1.4.0...v1.4.1 |
||||||
|
[1.4.0]: https://github.com/urfave/cli/compare/v1.3.1...v1.4.0 |
||||||
|
[1.3.1]: https://github.com/urfave/cli/compare/v1.3.0...v1.3.1 |
||||||
|
[1.3.0]: https://github.com/urfave/cli/compare/v1.2.0...v1.3.0 |
||||||
|
[1.2.0]: https://github.com/urfave/cli/compare/v1.1.0...v1.2.0 |
||||||
|
[1.1.0]: https://github.com/urfave/cli/compare/v1.0.0...v1.1.0 |
||||||
|
[1.0.0]: https://github.com/urfave/cli/compare/v0.1.0...v1.0.0 |
@ -0,0 +1,21 @@ |
|||||||
|
MIT License |
||||||
|
|
||||||
|
Copyright (c) 2016 Jeremy Saenz & Contributors |
||||||
|
|
||||||
|
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. |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,497 @@ |
|||||||
|
package cli |
||||||
|
|
||||||
|
import ( |
||||||
|
"fmt" |
||||||
|
"io" |
||||||
|
"io/ioutil" |
||||||
|
"os" |
||||||
|
"path/filepath" |
||||||
|
"sort" |
||||||
|
"time" |
||||||
|
) |
||||||
|
|
||||||
|
var ( |
||||||
|
changeLogURL = "https://github.com/urfave/cli/blob/master/CHANGELOG.md" |
||||||
|
appActionDeprecationURL = fmt.Sprintf("%s#deprecated-cli-app-action-signature", changeLogURL) |
||||||
|
runAndExitOnErrorDeprecationURL = fmt.Sprintf("%s#deprecated-cli-app-runandexitonerror", changeLogURL) |
||||||
|
|
||||||
|
contactSysadmin = "This is an error in the application. Please contact the distributor of this application if this is not you." |
||||||
|
|
||||||
|
errInvalidActionType = NewExitError("ERROR invalid Action type. "+ |
||||||
|
fmt.Sprintf("Must be `func(*Context`)` or `func(*Context) error). %s", contactSysadmin)+ |
||||||
|
fmt.Sprintf("See %s", appActionDeprecationURL), 2) |
||||||
|
) |
||||||
|
|
||||||
|
// App is the main structure of a cli application. It is recommended that
|
||||||
|
// an app be created with the cli.NewApp() function
|
||||||
|
type App struct { |
||||||
|
// The name of the program. Defaults to path.Base(os.Args[0])
|
||||||
|
Name string |
||||||
|
// Full name of command for help, defaults to Name
|
||||||
|
HelpName string |
||||||
|
// Description of the program.
|
||||||
|
Usage string |
||||||
|
// Text to override the USAGE section of help
|
||||||
|
UsageText string |
||||||
|
// Description of the program argument format.
|
||||||
|
ArgsUsage string |
||||||
|
// Version of the program
|
||||||
|
Version string |
||||||
|
// Description of the program
|
||||||
|
Description string |
||||||
|
// List of commands to execute
|
||||||
|
Commands []Command |
||||||
|
// List of flags to parse
|
||||||
|
Flags []Flag |
||||||
|
// Boolean to enable bash completion commands
|
||||||
|
EnableBashCompletion bool |
||||||
|
// Boolean to hide built-in help command
|
||||||
|
HideHelp bool |
||||||
|
// Boolean to hide built-in version flag and the VERSION section of help
|
||||||
|
HideVersion bool |
||||||
|
// Populate on app startup, only gettable through method Categories()
|
||||||
|
categories CommandCategories |
||||||
|
// An action to execute when the bash-completion flag is set
|
||||||
|
BashComplete BashCompleteFunc |
||||||
|
// An action to execute before any subcommands are run, but after the context is ready
|
||||||
|
// If a non-nil error is returned, no subcommands are run
|
||||||
|
Before BeforeFunc |
||||||
|
// An action to execute after any subcommands are run, but after the subcommand has finished
|
||||||
|
// It is run even if Action() panics
|
||||||
|
After AfterFunc |
||||||
|
|
||||||
|
// The action to execute when no subcommands are specified
|
||||||
|
// Expects a `cli.ActionFunc` but will accept the *deprecated* signature of `func(*cli.Context) {}`
|
||||||
|
// *Note*: support for the deprecated `Action` signature will be removed in a future version
|
||||||
|
Action interface{} |
||||||
|
|
||||||
|
// Execute this function if the proper command cannot be found
|
||||||
|
CommandNotFound CommandNotFoundFunc |
||||||
|
// Execute this function if an usage error occurs
|
||||||
|
OnUsageError OnUsageErrorFunc |
||||||
|
// Compilation date
|
||||||
|
Compiled time.Time |
||||||
|
// List of all authors who contributed
|
||||||
|
Authors []Author |
||||||
|
// Copyright of the binary if any
|
||||||
|
Copyright string |
||||||
|
// Name of Author (Note: Use App.Authors, this is deprecated)
|
||||||
|
Author string |
||||||
|
// Email of Author (Note: Use App.Authors, this is deprecated)
|
||||||
|
Email string |
||||||
|
// Writer writer to write output to
|
||||||
|
Writer io.Writer |
||||||
|
// ErrWriter writes error output
|
||||||
|
ErrWriter io.Writer |
||||||
|
// Other custom info
|
||||||
|
Metadata map[string]interface{} |
||||||
|
// Carries a function which returns app specific info.
|
||||||
|
ExtraInfo func() map[string]string |
||||||
|
// CustomAppHelpTemplate the text template for app help topic.
|
||||||
|
// cli.go uses text/template to render templates. You can
|
||||||
|
// render custom help text by setting this variable.
|
||||||
|
CustomAppHelpTemplate string |
||||||
|
|
||||||
|
didSetup bool |
||||||
|
} |
||||||
|
|
||||||
|
// Tries to find out when this binary was compiled.
|
||||||
|
// Returns the current time if it fails to find it.
|
||||||
|
func compileTime() time.Time { |
||||||
|
info, err := os.Stat(os.Args[0]) |
||||||
|
if err != nil { |
||||||
|
return time.Now() |
||||||
|
} |
||||||
|
return info.ModTime() |
||||||
|
} |
||||||
|
|
||||||
|
// NewApp creates a new cli Application with some reasonable defaults for Name,
|
||||||
|
// Usage, Version and Action.
|
||||||
|
func NewApp() *App { |
||||||
|
return &App{ |
||||||
|
Name: filepath.Base(os.Args[0]), |
||||||
|
HelpName: filepath.Base(os.Args[0]), |
||||||
|
Usage: "A new cli application", |
||||||
|
UsageText: "", |
||||||
|
Version: "0.0.0", |
||||||
|
BashComplete: DefaultAppComplete, |
||||||
|
Action: helpCommand.Action, |
||||||
|
Compiled: compileTime(), |
||||||
|
Writer: os.Stdout, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Setup runs initialization code to ensure all data structures are ready for
|
||||||
|
// `Run` or inspection prior to `Run`. It is internally called by `Run`, but
|
||||||
|
// will return early if setup has already happened.
|
||||||
|
func (a *App) Setup() { |
||||||
|
if a.didSetup { |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
a.didSetup = true |
||||||
|
|
||||||
|
if a.Author != "" || a.Email != "" { |
||||||
|
a.Authors = append(a.Authors, Author{Name: a.Author, Email: a.Email}) |
||||||
|
} |
||||||
|
|
||||||
|
newCmds := []Command{} |
||||||
|
for _, c := range a.Commands { |
||||||
|
if c.HelpName == "" { |
||||||
|
c.HelpName = fmt.Sprintf("%s %s", a.HelpName, c.Name) |
||||||
|
} |
||||||
|
newCmds = append(newCmds, c) |
||||||
|
} |
||||||
|
a.Commands = newCmds |
||||||
|
|
||||||
|
if a.Command(helpCommand.Name) == nil && !a.HideHelp { |
||||||
|
a.Commands = append(a.Commands, helpCommand) |
||||||
|
if (HelpFlag != BoolFlag{}) { |
||||||
|
a.appendFlag(HelpFlag) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if !a.HideVersion { |
||||||
|
a.appendFlag(VersionFlag) |
||||||
|
} |
||||||
|
|
||||||
|
a.categories = CommandCategories{} |
||||||
|
for _, command := range a.Commands { |
||||||
|
a.categories = a.categories.AddCommand(command.Category, command) |
||||||
|
} |
||||||
|
sort.Sort(a.categories) |
||||||
|
|
||||||
|
if a.Metadata == nil { |
||||||
|
a.Metadata = make(map[string]interface{}) |
||||||
|
} |
||||||
|
|
||||||
|
if a.Writer == nil { |
||||||
|
a.Writer = os.Stdout |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Run is the entry point to the cli app. Parses the arguments slice and routes
|
||||||
|
// to the proper flag/args combination
|
||||||
|
func (a *App) Run(arguments []string) (err error) { |
||||||
|
a.Setup() |
||||||
|
|
||||||
|
// handle the completion flag separately from the flagset since
|
||||||
|
// completion could be attempted after a flag, but before its value was put
|
||||||
|
// on the command line. this causes the flagset to interpret the completion
|
||||||
|
// flag name as the value of the flag before it which is undesirable
|
||||||
|
// note that we can only do this because the shell autocomplete function
|
||||||
|
// always appends the completion flag at the end of the command
|
||||||
|
shellComplete, arguments := checkShellCompleteFlag(a, arguments) |
||||||
|
|
||||||
|
// parse flags
|
||||||
|
set, err := flagSet(a.Name, a.Flags) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
|
||||||
|
set.SetOutput(ioutil.Discard) |
||||||
|
err = set.Parse(arguments[1:]) |
||||||
|
nerr := normalizeFlags(a.Flags, set) |
||||||
|
context := NewContext(a, set, nil) |
||||||
|
if nerr != nil { |
||||||
|
fmt.Fprintln(a.Writer, nerr) |
||||||
|
ShowAppHelp(context) |
||||||
|
return nerr |
||||||
|
} |
||||||
|
context.shellComplete = shellComplete |
||||||
|
|
||||||
|
if checkCompletions(context) { |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
if err != nil { |
||||||
|
if a.OnUsageError != nil { |
||||||
|
err := a.OnUsageError(context, err, false) |
||||||
|
HandleExitCoder(err) |
||||||
|
return err |
||||||
|
} |
||||||
|
fmt.Fprintf(a.Writer, "%s %s\n\n", "Incorrect Usage.", err.Error()) |
||||||
|
ShowAppHelp(context) |
||||||
|
return err |
||||||
|
} |
||||||
|
|
||||||
|
if !a.HideHelp && checkHelp(context) { |
||||||
|
ShowAppHelp(context) |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
if !a.HideVersion && checkVersion(context) { |
||||||
|
ShowVersion(context) |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
if a.After != nil { |
||||||
|
defer func() { |
||||||
|
if afterErr := a.After(context); afterErr != nil { |
||||||
|
if err != nil { |
||||||
|
err = NewMultiError(err, afterErr) |
||||||
|
} else { |
||||||
|
err = afterErr |
||||||
|
} |
||||||
|
} |
||||||
|
}() |
||||||
|
} |
||||||
|
|
||||||
|
if a.Before != nil { |
||||||
|
beforeErr := a.Before(context) |
||||||
|
if beforeErr != nil { |
||||||
|
ShowAppHelp(context) |
||||||
|
HandleExitCoder(beforeErr) |
||||||
|
err = beforeErr |
||||||
|
return err |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
args := context.Args() |
||||||
|
if args.Present() { |
||||||
|
name := args.First() |
||||||
|
c := a.Command(name) |
||||||
|
if c != nil { |
||||||
|
return c.Run(context) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if a.Action == nil { |
||||||
|
a.Action = helpCommand.Action |
||||||
|
} |
||||||
|
|
||||||
|
// Run default Action
|
||||||
|
err = HandleAction(a.Action, context) |
||||||
|
|
||||||
|
HandleExitCoder(err) |
||||||
|
return err |
||||||
|
} |
||||||
|
|
||||||
|
// RunAndExitOnError calls .Run() and exits non-zero if an error was returned
|
||||||
|
//
|
||||||
|
// Deprecated: instead you should return an error that fulfills cli.ExitCoder
|
||||||
|
// to cli.App.Run. This will cause the application to exit with the given eror
|
||||||
|
// code in the cli.ExitCoder
|
||||||
|
func (a *App) RunAndExitOnError() { |
||||||
|
if err := a.Run(os.Args); err != nil { |
||||||
|
fmt.Fprintln(a.errWriter(), err) |
||||||
|
OsExiter(1) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// RunAsSubcommand invokes the subcommand given the context, parses ctx.Args() to
|
||||||
|
// generate command-specific flags
|
||||||
|
func (a *App) RunAsSubcommand(ctx *Context) (err error) { |
||||||
|
// append help to commands
|
||||||
|
if len(a.Commands) > 0 { |
||||||
|
if a.Command(helpCommand.Name) == nil && !a.HideHelp { |
||||||
|
a.Commands = append(a.Commands, helpCommand) |
||||||
|
if (HelpFlag != BoolFlag{}) { |
||||||
|
a.appendFlag(HelpFlag) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
newCmds := []Command{} |
||||||
|
for _, c := range a.Commands { |
||||||
|
if c.HelpName == "" { |
||||||
|
c.HelpName = fmt.Sprintf("%s %s", a.HelpName, c.Name) |
||||||
|
} |
||||||
|
newCmds = append(newCmds, c) |
||||||
|
} |
||||||
|
a.Commands = newCmds |
||||||
|
|
||||||
|
// parse flags
|
||||||
|
set, err := flagSet(a.Name, a.Flags) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
|
||||||
|
set.SetOutput(ioutil.Discard) |
||||||
|
err = set.Parse(ctx.Args().Tail()) |
||||||
|
nerr := normalizeFlags(a.Flags, set) |
||||||
|
context := NewContext(a, set, ctx) |
||||||
|
|
||||||
|
if nerr != nil { |
||||||
|
fmt.Fprintln(a.Writer, nerr) |
||||||
|
fmt.Fprintln(a.Writer) |
||||||
|
if len(a.Commands) > 0 { |
||||||
|
ShowSubcommandHelp(context) |
||||||
|
} else { |
||||||
|
ShowCommandHelp(ctx, context.Args().First()) |
||||||
|
} |
||||||
|
return nerr |
||||||
|
} |
||||||
|
|
||||||
|
if checkCompletions(context) { |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
if err != nil { |
||||||
|
if a.OnUsageError != nil { |
||||||
|
err = a.OnUsageError(context, err, true) |
||||||
|
HandleExitCoder(err) |
||||||
|
return err |
||||||
|
} |
||||||
|
fmt.Fprintf(a.Writer, "%s %s\n\n", "Incorrect Usage.", err.Error()) |
||||||
|
ShowSubcommandHelp(context) |
||||||
|
return err |
||||||
|
} |
||||||
|
|
||||||
|
if len(a.Commands) > 0 { |
||||||
|
if checkSubcommandHelp(context) { |
||||||
|
return nil |
||||||
|
} |
||||||
|
} else { |
||||||
|
if checkCommandHelp(ctx, context.Args().First()) { |
||||||
|
return nil |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if a.After != nil { |
||||||
|
defer func() { |
||||||
|
afterErr := a.After(context) |
||||||
|
if afterErr != nil { |
||||||
|
HandleExitCoder(err) |
||||||
|
if err != nil { |
||||||
|
err = NewMultiError(err, afterErr) |
||||||
|
} else { |
||||||
|
err = afterErr |
||||||
|
} |
||||||
|
} |
||||||
|
}() |
||||||
|
} |
||||||
|
|
||||||
|
if a.Before != nil { |
||||||
|
beforeErr := a.Before(context) |
||||||
|
if beforeErr != nil { |
||||||
|
HandleExitCoder(beforeErr) |
||||||
|
err = beforeErr |
||||||
|
return err |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
args := context.Args() |
||||||
|
if args.Present() { |
||||||
|
name := args.First() |
||||||
|
c := a.Command(name) |
||||||
|
if c != nil { |
||||||
|
return c.Run(context) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Run default Action
|
||||||
|
err = HandleAction(a.Action, context) |
||||||
|
|
||||||
|
HandleExitCoder(err) |
||||||
|
return err |
||||||
|
} |
||||||
|
|
||||||
|
// Command returns the named command on App. Returns nil if the command does not exist
|
||||||
|
func (a *App) Command(name string) *Command { |
||||||
|
for _, c := range a.Commands { |
||||||
|
if c.HasName(name) { |
||||||
|
return &c |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// Categories returns a slice containing all the categories with the commands they contain
|
||||||
|
func (a *App) Categories() CommandCategories { |
||||||
|
return a.categories |
||||||
|
} |
||||||
|
|
||||||
|
// VisibleCategories returns a slice of categories and commands that are
|
||||||
|
// Hidden=false
|
||||||
|
func (a *App) VisibleCategories() []*CommandCategory { |
||||||
|
ret := []*CommandCategory{} |
||||||
|
for _, category := range a.categories { |
||||||
|
if visible := func() *CommandCategory { |
||||||
|
for _, command := range category.Commands { |
||||||
|
if !command.Hidden { |
||||||
|
return category |
||||||
|
} |
||||||
|
} |
||||||
|
return nil |
||||||
|
}(); visible != nil { |
||||||
|
ret = append(ret, visible) |
||||||
|
} |
||||||
|
} |
||||||
|
return ret |
||||||
|
} |
||||||
|
|
||||||
|
// VisibleCommands returns a slice of the Commands with Hidden=false
|
||||||
|
func (a *App) VisibleCommands() []Command { |
||||||
|
ret := []Command{} |
||||||
|
for _, command := range a.Commands { |
||||||
|
if !command.Hidden { |
||||||
|
ret = append(ret, command) |
||||||
|
} |
||||||
|
} |
||||||
|
return ret |
||||||
|
} |
||||||
|
|
||||||
|
// VisibleFlags returns a slice of the Flags with Hidden=false
|
||||||
|
func (a *App) VisibleFlags() []Flag { |
||||||
|
return visibleFlags(a.Flags) |
||||||
|
} |
||||||
|
|
||||||
|
func (a *App) hasFlag(flag Flag) bool { |
||||||
|
for _, f := range a.Flags { |
||||||
|
if flag == f { |
||||||
|
return true |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return false |
||||||
|
} |
||||||
|
|
||||||
|
func (a *App) errWriter() io.Writer { |
||||||
|
|
||||||
|
// When the app ErrWriter is nil use the package level one.
|
||||||
|
if a.ErrWriter == nil { |
||||||
|
return ErrWriter |
||||||
|
} |
||||||
|
|
||||||
|
return a.ErrWriter |
||||||
|
} |
||||||
|
|
||||||
|
func (a *App) appendFlag(flag Flag) { |
||||||
|
if !a.hasFlag(flag) { |
||||||
|
a.Flags = append(a.Flags, flag) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Author represents someone who has contributed to a cli project.
|
||||||
|
type Author struct { |
||||||
|
Name string // The Authors name
|
||||||
|
Email string // The Authors email
|
||||||
|
} |
||||||
|
|
||||||
|
// String makes Author comply to the Stringer interface, to allow an easy print in the templating process
|
||||||
|
func (a Author) String() string { |
||||||
|
e := "" |
||||||
|
if a.Email != "" { |
||||||
|
e = " <" + a.Email + ">" |
||||||
|
} |
||||||
|
|
||||||
|
return fmt.Sprintf("%v%v", a.Name, e) |
||||||
|
} |
||||||
|
|
||||||
|
// HandleAction attempts to figure out which Action signature was used. If
|
||||||
|
// it's an ActionFunc or a func with the legacy signature for Action, the func
|
||||||
|
// is run!
|
||||||
|
func HandleAction(action interface{}, context *Context) (err error) { |
||||||
|
if a, ok := action.(ActionFunc); ok { |
||||||
|
return a(context) |
||||||
|
} else if a, ok := action.(func(*Context) error); ok { |
||||||
|
return a(context) |
||||||
|
} else if a, ok := action.(func(*Context)); ok { // deprecated function signature
|
||||||
|
a(context) |
||||||
|
return nil |
||||||
|
} else { |
||||||
|
return errInvalidActionType |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,26 @@ |
|||||||
|
version: "{build}" |
||||||
|
|
||||||
|
os: Windows Server 2016 |
||||||
|
|
||||||
|
image: Visual Studio 2017 |
||||||
|
|
||||||
|
clone_folder: c:\gopath\src\github.com\urfave\cli |
||||||
|
|
||||||
|
environment: |
||||||
|
GOPATH: C:\gopath |
||||||
|
GOVERSION: 1.8.x |
||||||
|
PYTHON: C:\Python36-x64 |
||||||
|
PYTHON_VERSION: 3.6.x |
||||||
|
PYTHON_ARCH: 64 |
||||||
|
|
||||||
|
install: |
||||||
|
- set PATH=%GOPATH%\bin;C:\go\bin;%PATH% |
||||||
|
- go version |
||||||
|
- go env |
||||||
|
- go get github.com/urfave/gfmrun/... |
||||||
|
- go get -v -t ./... |
||||||
|
|
||||||
|
build_script: |
||||||
|
- python runtests vet |
||||||
|
- python runtests test |
||||||
|
- python runtests gfmrun |
@ -0,0 +1,44 @@ |
|||||||
|
package cli |
||||||
|
|
||||||
|
// CommandCategories is a slice of *CommandCategory.
|
||||||
|
type CommandCategories []*CommandCategory |
||||||
|
|
||||||
|
// CommandCategory is a category containing commands.
|
||||||
|
type CommandCategory struct { |
||||||
|
Name string |
||||||
|
Commands Commands |
||||||
|
} |
||||||
|
|
||||||
|
func (c CommandCategories) Less(i, j int) bool { |
||||||
|
return c[i].Name < c[j].Name |
||||||
|
} |
||||||
|
|
||||||
|
func (c CommandCategories) Len() int { |
||||||
|
return len(c) |
||||||
|
} |
||||||
|
|
||||||
|
func (c CommandCategories) Swap(i, j int) { |
||||||
|
c[i], c[j] = c[j], c[i] |
||||||
|
} |
||||||
|
|
||||||
|
// AddCommand adds a command to a category.
|
||||||
|
func (c CommandCategories) AddCommand(category string, command Command) CommandCategories { |
||||||
|
for _, commandCategory := range c { |
||||||
|
if commandCategory.Name == category { |
||||||
|
commandCategory.Commands = append(commandCategory.Commands, command) |
||||||
|
return c |
||||||
|
} |
||||||
|
} |
||||||
|
return append(c, &CommandCategory{Name: category, Commands: []Command{command}}) |
||||||
|
} |
||||||
|
|
||||||
|
// VisibleCommands returns a slice of the Commands with Hidden=false
|
||||||
|
func (c *CommandCategory) VisibleCommands() []Command { |
||||||
|
ret := []Command{} |
||||||
|
for _, command := range c.Commands { |
||||||
|
if !command.Hidden { |
||||||
|
ret = append(ret, command) |
||||||
|
} |
||||||
|
} |
||||||
|
return ret |
||||||
|
} |
@ -0,0 +1,22 @@ |
|||||||
|
// Package cli provides a minimal framework for creating and organizing command line
|
||||||
|
// Go applications. cli is designed to be easy to understand and write, the most simple
|
||||||
|
// cli application can be written as follows:
|
||||||
|
// func main() {
|
||||||
|
// cli.NewApp().Run(os.Args)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Of course this application does not do much, so let's make this an actual application:
|
||||||
|
// func main() {
|
||||||
|
// app := cli.NewApp()
|
||||||
|
// app.Name = "greet"
|
||||||
|
// app.Usage = "say a greeting"
|
||||||
|
// app.Action = func(c *cli.Context) error {
|
||||||
|
// println("Greetings")
|
||||||
|
// return nil
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// app.Run(os.Args)
|
||||||
|
// }
|
||||||
|
package cli |
||||||
|
|
||||||
|
//go:generate python ./generate-flag-types cli -i flag-types.json -o flag_generated.go
|
@ -0,0 +1,304 @@ |
|||||||
|
package cli |
||||||
|
|
||||||
|
import ( |
||||||
|
"fmt" |
||||||
|
"io/ioutil" |
||||||
|
"sort" |
||||||
|
"strings" |
||||||
|
) |
||||||
|
|
||||||
|
// Command is a subcommand for a cli.App.
|
||||||
|
type Command struct { |
||||||
|
// The name of the command
|
||||||
|
Name string |
||||||
|
// short name of the command. Typically one character (deprecated, use `Aliases`)
|
||||||
|
ShortName string |
||||||
|
// A list of aliases for the command
|
||||||
|
Aliases []string |
||||||
|
// A short description of the usage of this command
|
||||||
|
Usage string |
||||||
|
// Custom text to show on USAGE section of help
|
||||||
|
UsageText string |
||||||
|
// A longer explanation of how the command works
|
||||||
|
Description string |
||||||
|
// A short description of the arguments of this command
|
||||||
|
ArgsUsage string |
||||||
|
// The category the command is part of
|
||||||
|
Category string |
||||||
|
// The function to call when checking for bash command completions
|
||||||
|
BashComplete BashCompleteFunc |
||||||
|
// An action to execute before any sub-subcommands are run, but after the context is ready
|
||||||
|
// If a non-nil error is returned, no sub-subcommands are run
|
||||||
|
Before BeforeFunc |
||||||
|
// An action to execute after any subcommands are run, but after the subcommand has finished
|
||||||
|
// It is run even if Action() panics
|
||||||
|
After AfterFunc |
||||||
|
// The function to call when this command is invoked
|
||||||
|
Action interface{} |
||||||
|
// TODO: replace `Action: interface{}` with `Action: ActionFunc` once some kind
|
||||||
|
// of deprecation period has passed, maybe?
|
||||||
|
|
||||||
|
// Execute this function if a usage error occurs.
|
||||||
|
OnUsageError OnUsageErrorFunc |
||||||
|
// List of child commands
|
||||||
|
Subcommands Commands |
||||||
|
// List of flags to parse
|
||||||
|
Flags []Flag |
||||||
|
// Treat all flags as normal arguments if true
|
||||||
|
SkipFlagParsing bool |
||||||
|
// Skip argument reordering which attempts to move flags before arguments,
|
||||||
|
// but only works if all flags appear after all arguments. This behavior was
|
||||||
|
// removed n version 2 since it only works under specific conditions so we
|
||||||
|
// backport here by exposing it as an option for compatibility.
|
||||||
|
SkipArgReorder bool |
||||||
|
// Boolean to hide built-in help command
|
||||||
|
HideHelp bool |
||||||
|
// Boolean to hide this command from help or completion
|
||||||
|
Hidden bool |
||||||
|
|
||||||
|
// Full name of command for help, defaults to full command name, including parent commands.
|
||||||
|
HelpName string |
||||||
|
commandNamePath []string |
||||||
|
|
||||||
|
// CustomHelpTemplate the text template for the command help topic.
|
||||||
|
// cli.go uses text/template to render templates. You can
|
||||||
|
// render custom help text by setting this variable.
|
||||||
|
CustomHelpTemplate string |
||||||
|
} |
||||||
|
|
||||||
|
type CommandsByName []Command |
||||||
|
|
||||||
|
func (c CommandsByName) Len() int { |
||||||
|
return len(c) |
||||||
|
} |
||||||
|
|
||||||
|
func (c CommandsByName) Less(i, j int) bool { |
||||||
|
return c[i].Name < c[j].Name |
||||||
|
} |
||||||
|
|
||||||
|
func (c CommandsByName) Swap(i, j int) { |
||||||
|
c[i], c[j] = c[j], c[i] |
||||||
|
} |
||||||
|
|
||||||
|
// FullName returns the full name of the command.
|
||||||
|
// For subcommands this ensures that parent commands are part of the command path
|
||||||
|
func (c Command) FullName() string { |
||||||
|
if c.commandNamePath == nil { |
||||||
|
return c.Name |
||||||
|
} |
||||||
|
return strings.Join(c.commandNamePath, " ") |
||||||
|
} |
||||||
|
|
||||||
|
// Commands is a slice of Command
|
||||||
|
type Commands []Command |
||||||
|
|
||||||
|
// Run invokes the command given the context, parses ctx.Args() to generate command-specific flags
|
||||||
|
func (c Command) Run(ctx *Context) (err error) { |
||||||
|
if len(c.Subcommands) > 0 { |
||||||
|
return c.startApp(ctx) |
||||||
|
} |
||||||
|
|
||||||
|
if !c.HideHelp && (HelpFlag != BoolFlag{}) { |
||||||
|
// append help to flags
|
||||||
|
c.Flags = append( |
||||||
|
c.Flags, |
||||||
|
HelpFlag, |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
set, err := flagSet(c.Name, c.Flags) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
set.SetOutput(ioutil.Discard) |
||||||
|
|
||||||
|
if c.SkipFlagParsing { |
||||||
|
err = set.Parse(append([]string{"--"}, ctx.Args().Tail()...)) |
||||||
|
} else if !c.SkipArgReorder { |
||||||
|
firstFlagIndex := -1 |
||||||
|
terminatorIndex := -1 |
||||||
|
for index, arg := range ctx.Args() { |
||||||
|
if arg == "--" { |
||||||
|
terminatorIndex = index |
||||||
|
break |
||||||
|
} else if arg == "-" { |
||||||
|
// Do nothing. A dash alone is not really a flag.
|
||||||
|
continue |
||||||
|
} else if strings.HasPrefix(arg, "-") && firstFlagIndex == -1 { |
||||||
|
firstFlagIndex = index |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if firstFlagIndex > -1 { |
||||||
|
args := ctx.Args() |
||||||
|
regularArgs := make([]string, len(args[1:firstFlagIndex])) |
||||||
|
copy(regularArgs, args[1:firstFlagIndex]) |
||||||
|
|
||||||
|
var flagArgs []string |
||||||
|
if terminatorIndex > -1 { |
||||||
|
flagArgs = args[firstFlagIndex:terminatorIndex] |
||||||
|
regularArgs = append(regularArgs, args[terminatorIndex:]...) |
||||||
|
} else { |
||||||
|
flagArgs = args[firstFlagIndex:] |
||||||
|
} |
||||||
|
|
||||||
|
err = set.Parse(append(flagArgs, regularArgs...)) |
||||||
|
} else { |
||||||
|
err = set.Parse(ctx.Args().Tail()) |
||||||
|
} |
||||||
|
} else { |
||||||
|
err = set.Parse(ctx.Args().Tail()) |
||||||
|
} |
||||||
|
|
||||||
|
nerr := normalizeFlags(c.Flags, set) |
||||||
|
if nerr != nil { |
||||||
|
fmt.Fprintln(ctx.App.Writer, nerr) |
||||||
|
fmt.Fprintln(ctx.App.Writer) |
||||||
|
ShowCommandHelp(ctx, c.Name) |
||||||
|
return nerr |
||||||
|
} |
||||||
|
|
||||||
|
context := NewContext(ctx.App, set, ctx) |
||||||
|
context.Command = c |
||||||
|
if checkCommandCompletions(context, c.Name) { |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
if err != nil { |
||||||
|
if c.OnUsageError != nil { |
||||||
|
err := c.OnUsageError(context, err, false) |
||||||
|
HandleExitCoder(err) |
||||||
|
return err |
||||||
|
} |
||||||
|
fmt.Fprintln(context.App.Writer, "Incorrect Usage:", err.Error()) |
||||||
|
fmt.Fprintln(context.App.Writer) |
||||||
|
ShowCommandHelp(context, c.Name) |
||||||
|
return err |
||||||
|
} |
||||||
|
|
||||||
|
if checkCommandHelp(context, c.Name) { |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
if c.After != nil { |
||||||
|
defer func() { |
||||||
|
afterErr := c.After(context) |
||||||
|
if afterErr != nil { |
||||||
|
HandleExitCoder(err) |
||||||
|
if err != nil { |
||||||
|
err = NewMultiError(err, afterErr) |
||||||
|
} else { |
||||||
|
err = afterErr |
||||||
|
} |
||||||
|
} |
||||||
|
}() |
||||||
|
} |
||||||
|
|
||||||
|
if c.Before != nil { |
||||||
|
err = c.Before(context) |
||||||
|
if err != nil { |
||||||
|
ShowCommandHelp(context, c.Name) |
||||||
|
HandleExitCoder(err) |
||||||
|
return err |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if c.Action == nil { |
||||||
|
c.Action = helpSubcommand.Action |
||||||
|
} |
||||||
|
|
||||||
|
err = HandleAction(c.Action, context) |
||||||
|
|
||||||
|
if err != nil { |
||||||
|
HandleExitCoder(err) |
||||||
|
} |
||||||
|
return err |
||||||
|
} |
||||||
|
|
||||||
|
// Names returns the names including short names and aliases.
|
||||||
|
func (c Command) Names() []string { |
||||||
|
names := []string{c.Name} |
||||||
|
|
||||||
|
if c.ShortName != "" { |
||||||
|
names = append(names, c.ShortName) |
||||||
|
} |
||||||
|
|
||||||
|
return append(names, c.Aliases...) |
||||||
|
} |
||||||
|
|
||||||
|
// HasName returns true if Command.Name or Command.ShortName matches given name
|
||||||
|
func (c Command) HasName(name string) bool { |
||||||
|
for _, n := range c.Names() { |
||||||
|
if n == name { |
||||||
|
return true |
||||||
|
} |
||||||
|
} |
||||||
|
return false |
||||||
|
} |
||||||
|
|
||||||
|
func (c Command) startApp(ctx *Context) error { |
||||||
|
app := NewApp() |
||||||
|
app.Metadata = ctx.App.Metadata |
||||||
|
// set the name and usage
|
||||||
|
app.Name = fmt.Sprintf("%s %s", ctx.App.Name, c.Name) |
||||||
|
if c.HelpName == "" { |
||||||
|
app.HelpName = c.HelpName |
||||||
|
} else { |
||||||
|
app.HelpName = app.Name |
||||||
|
} |
||||||
|
|
||||||
|
app.Usage = c.Usage |
||||||
|
app.Description = c.Description |
||||||
|
app.ArgsUsage = c.ArgsUsage |
||||||
|
|
||||||
|
// set CommandNotFound
|
||||||
|
app.CommandNotFound = ctx.App.CommandNotFound |
||||||
|
app.CustomAppHelpTemplate = c.CustomHelpTemplate |
||||||
|
|
||||||
|
// set the flags and commands
|
||||||
|
app.Commands = c.Subcommands |
||||||
|
app.Flags = c.Flags |
||||||
|
app.HideHelp = c.HideHelp |
||||||
|
|
||||||
|
app.Version = ctx.App.Version |
||||||
|
app.HideVersion = ctx.App.HideVersion |
||||||
|
app.Compiled = ctx.App.Compiled |
||||||
|
app.Author = ctx.App.Author |
||||||
|
app.Email = ctx.App.Email |
||||||
|
app.Writer = ctx.App.Writer |
||||||
|
app.ErrWriter = ctx.App.ErrWriter |
||||||
|
|
||||||
|
app.categories = CommandCategories{} |
||||||
|
for _, command := range c.Subcommands { |
||||||
|
app.categories = app.categories.AddCommand(command.Category, command) |
||||||
|
} |
||||||
|
|
||||||
|
sort.Sort(app.categories) |
||||||
|
|
||||||
|
// bash completion
|
||||||
|
app.EnableBashCompletion = ctx.App.EnableBashCompletion |
||||||
|
if c.BashComplete != nil { |
||||||
|
app.BashComplete = c.BashComplete |
||||||
|
} |
||||||
|
|
||||||
|
// set the actions
|
||||||
|
app.Before = c.Before |
||||||
|
app.After = c.After |
||||||
|
if c.Action != nil { |
||||||
|
app.Action = c.Action |
||||||
|
} else { |
||||||
|
app.Action = helpSubcommand.Action |
||||||
|
} |
||||||
|
app.OnUsageError = c.OnUsageError |
||||||
|
|
||||||
|
for index, cc := range app.Commands { |
||||||
|
app.Commands[index].commandNamePath = []string{c.Name, cc.Name} |
||||||
|
} |
||||||
|
|
||||||
|
return app.RunAsSubcommand(ctx) |
||||||
|
} |
||||||
|
|
||||||
|
// VisibleFlags returns a slice of the Flags with Hidden=false
|
||||||
|
func (c Command) VisibleFlags() []Flag { |
||||||
|
return visibleFlags(c.Flags) |
||||||
|
} |
@ -0,0 +1,278 @@ |
|||||||
|
package cli |
||||||
|
|
||||||
|
import ( |
||||||
|
"errors" |
||||||
|
"flag" |
||||||
|
"reflect" |
||||||
|
"strings" |
||||||
|
"syscall" |
||||||
|
) |
||||||
|
|
||||||
|
// Context is a type that is passed through to
|
||||||
|
// each Handler action in a cli application. Context
|
||||||
|
// can be used to retrieve context-specific Args and
|
||||||
|
// parsed command-line options.
|
||||||
|
type Context struct { |
||||||
|
App *App |
||||||
|
Command Command |
||||||
|
shellComplete bool |
||||||
|
flagSet *flag.FlagSet |
||||||
|
setFlags map[string]bool |
||||||
|
parentContext *Context |
||||||
|
} |
||||||
|
|
||||||
|
// NewContext creates a new context. For use in when invoking an App or Command action.
|
||||||
|
func NewContext(app *App, set *flag.FlagSet, parentCtx *Context) *Context { |
||||||
|
c := &Context{App: app, flagSet: set, parentContext: parentCtx} |
||||||
|
|
||||||
|
if parentCtx != nil { |
||||||
|
c.shellComplete = parentCtx.shellComplete |
||||||
|
} |
||||||
|
|
||||||
|
return c |
||||||
|
} |
||||||
|
|
||||||
|
// NumFlags returns the number of flags set
|
||||||
|
func (c *Context) NumFlags() int { |
||||||
|
return c.flagSet.NFlag() |
||||||
|
} |
||||||
|
|
||||||
|
// Set sets a context flag to a value.
|
||||||
|
func (c *Context) Set(name, value string) error { |
||||||
|
c.setFlags = nil |
||||||
|
return c.flagSet.Set(name, value) |
||||||
|
} |
||||||
|
|
||||||
|
// GlobalSet sets a context flag to a value on the global flagset
|
||||||
|
func (c *Context) GlobalSet(name, value string) error { |
||||||
|
globalContext(c).setFlags = nil |
||||||
|
return globalContext(c).flagSet.Set(name, value) |
||||||
|
} |
||||||
|
|
||||||
|
// IsSet determines if the flag was actually set
|
||||||
|
func (c *Context) IsSet(name string) bool { |
||||||
|
if c.setFlags == nil { |
||||||
|
c.setFlags = make(map[string]bool) |
||||||
|
|
||||||
|
c.flagSet.Visit(func(f *flag.Flag) { |
||||||
|
c.setFlags[f.Name] = true |
||||||
|
}) |
||||||
|
|
||||||
|
c.flagSet.VisitAll(func(f *flag.Flag) { |
||||||
|
if _, ok := c.setFlags[f.Name]; ok { |
||||||
|
return |
||||||
|
} |
||||||
|
c.setFlags[f.Name] = false |
||||||
|
}) |
||||||
|
|
||||||
|
// XXX hack to support IsSet for flags with EnvVar
|
||||||
|
//
|
||||||
|
// There isn't an easy way to do this with the current implementation since
|
||||||
|
// whether a flag was set via an environment variable is very difficult to
|
||||||
|
// determine here. Instead, we intend to introduce a backwards incompatible
|
||||||
|
// change in version 2 to add `IsSet` to the Flag interface to push the
|
||||||
|
// responsibility closer to where the information required to determine
|
||||||
|
// whether a flag is set by non-standard means such as environment
|
||||||
|
// variables is avaliable.
|
||||||
|
//
|
||||||
|
// See https://github.com/urfave/cli/issues/294 for additional discussion
|
||||||
|
flags := c.Command.Flags |
||||||
|
if c.Command.Name == "" { // cannot == Command{} since it contains slice types
|
||||||
|
if c.App != nil { |
||||||
|
flags = c.App.Flags |
||||||
|
} |
||||||
|
} |
||||||
|
for _, f := range flags { |
||||||
|
eachName(f.GetName(), func(name string) { |
||||||
|
if isSet, ok := c.setFlags[name]; isSet || !ok { |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
val := reflect.ValueOf(f) |
||||||
|
if val.Kind() == reflect.Ptr { |
||||||
|
val = val.Elem() |
||||||
|
} |
||||||
|
|
||||||
|
envVarValue := val.FieldByName("EnvVar") |
||||||
|
if !envVarValue.IsValid() { |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
eachName(envVarValue.String(), func(envVar string) { |
||||||
|
envVar = strings.TrimSpace(envVar) |
||||||
|
if _, ok := syscall.Getenv(envVar); ok { |
||||||
|
c.setFlags[name] = true |
||||||
|
return |
||||||
|
} |
||||||
|
}) |
||||||
|
}) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return c.setFlags[name] |
||||||
|
} |
||||||
|
|
||||||
|
// GlobalIsSet determines if the global flag was actually set
|
||||||
|
func (c *Context) GlobalIsSet(name string) bool { |
||||||
|
ctx := c |
||||||
|
if ctx.parentContext != nil { |
||||||
|
ctx = ctx.parentContext |
||||||
|
} |
||||||
|
|
||||||
|
for ; ctx != nil; ctx = ctx.parentContext { |
||||||
|
if ctx.IsSet(name) { |
||||||
|
return true |
||||||
|
} |
||||||
|
} |
||||||
|
return false |
||||||
|
} |
||||||
|
|
||||||
|
// FlagNames returns a slice of flag names used in this context.
|
||||||
|
func (c *Context) FlagNames() (names []string) { |
||||||
|
for _, flag := range c.Command.Flags { |
||||||
|
name := strings.Split(flag.GetName(), ",")[0] |
||||||
|
if name == "help" { |
||||||
|
continue |
||||||
|
} |
||||||
|
names = append(names, name) |
||||||
|
} |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
// GlobalFlagNames returns a slice of global flag names used by the app.
|
||||||
|
func (c *Context) GlobalFlagNames() (names []string) { |
||||||
|
for _, flag := range c.App.Flags { |
||||||
|
name := strings.Split(flag.GetName(), ",")[0] |
||||||
|
if name == "help" || name == "version" { |
||||||
|
continue |
||||||
|
} |
||||||
|
names = append(names, name) |
||||||
|
} |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
// Parent returns the parent context, if any
|
||||||
|
func (c *Context) Parent() *Context { |
||||||
|
return c.parentContext |
||||||
|
} |
||||||
|
|
||||||
|
// value returns the value of the flag coressponding to `name`
|
||||||
|
func (c *Context) value(name string) interface{} { |
||||||
|
return c.flagSet.Lookup(name).Value.(flag.Getter).Get() |
||||||
|
} |
||||||
|
|
||||||
|
// Args contains apps console arguments
|
||||||
|
type Args []string |
||||||
|
|
||||||
|
// Args returns the command line arguments associated with the context.
|
||||||
|
func (c *Context) Args() Args { |
||||||
|
args := Args(c.flagSet.Args()) |
||||||
|
return args |
||||||
|
} |
||||||
|
|
||||||
|
// NArg returns the number of the command line arguments.
|
||||||
|
func (c *Context) NArg() int { |
||||||
|
return len(c.Args()) |
||||||
|
} |
||||||
|
|
||||||
|
// Get returns the nth argument, or else a blank string
|
||||||
|
func (a Args) Get(n int) string { |
||||||
|
if len(a) > n { |
||||||
|
return a[n] |
||||||
|
} |
||||||
|
return "" |
||||||
|
} |
||||||
|
|
||||||
|
// First returns the first argument, or else a blank string
|
||||||
|
func (a Args) First() string { |
||||||
|
return a.Get(0) |
||||||
|
} |
||||||
|
|
||||||
|
// Tail returns the rest of the arguments (not the first one)
|
||||||
|
// or else an empty string slice
|
||||||
|
func (a Args) Tail() []string { |
||||||
|
if len(a) >= 2 { |
||||||
|
return []string(a)[1:] |
||||||
|
} |
||||||
|
return []string{} |
||||||
|
} |
||||||
|
|
||||||
|
// Present checks if there are any arguments present
|
||||||
|
func (a Args) Present() bool { |
||||||
|
return len(a) != 0 |
||||||
|
} |
||||||
|
|
||||||
|
// Swap swaps arguments at the given indexes
|
||||||
|
func (a Args) Swap(from, to int) error { |
||||||
|
if from >= len(a) || to >= len(a) { |
||||||
|
return errors.New("index out of range") |
||||||
|
} |
||||||
|
a[from], a[to] = a[to], a[from] |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
func globalContext(ctx *Context) *Context { |
||||||
|
if ctx == nil { |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
for { |
||||||
|
if ctx.parentContext == nil { |
||||||
|
return ctx |
||||||
|
} |
||||||
|
ctx = ctx.parentContext |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func lookupGlobalFlagSet(name string, ctx *Context) *flag.FlagSet { |
||||||
|
if ctx.parentContext != nil { |
||||||
|
ctx = ctx.parentContext |
||||||
|
} |
||||||
|
for ; ctx != nil; ctx = ctx.parentContext { |
||||||
|
if f := ctx.flagSet.Lookup(name); f != nil { |
||||||
|
return ctx.flagSet |
||||||
|
} |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
func copyFlag(name string, ff *flag.Flag, set *flag.FlagSet) { |
||||||
|
switch ff.Value.(type) { |
||||||
|
case *StringSlice: |
||||||
|
default: |
||||||
|
set.Set(name, ff.Value.String()) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func normalizeFlags(flags []Flag, set *flag.FlagSet) error { |
||||||
|
visited := make(map[string]bool) |
||||||
|
set.Visit(func(f *flag.Flag) { |
||||||
|
visited[f.Name] = true |
||||||
|
}) |
||||||
|
for _, f := range flags { |
||||||
|
parts := strings.Split(f.GetName(), ",") |
||||||
|
if len(parts) == 1 { |
||||||
|
continue |
||||||
|
} |
||||||
|
var ff *flag.Flag |
||||||
|
for _, name := range parts { |
||||||
|
name = strings.Trim(name, " ") |
||||||
|
if visited[name] { |
||||||
|
if ff != nil { |
||||||
|
return errors.New("Cannot use two forms of the same flag: " + name + " " + ff.Name) |
||||||
|
} |
||||||
|
ff = set.Lookup(name) |
||||||
|
} |
||||||
|
} |
||||||
|
if ff == nil { |
||||||
|
continue |
||||||
|
} |
||||||
|
for _, name := range parts { |
||||||
|
name = strings.Trim(name, " ") |
||||||
|
if !visited[name] { |
||||||
|
copyFlag(name, ff, set) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
@ -0,0 +1,115 @@ |
|||||||
|
package cli |
||||||
|
|
||||||
|
import ( |
||||||
|
"fmt" |
||||||
|
"io" |
||||||
|
"os" |
||||||
|
"strings" |
||||||
|
) |
||||||
|
|
||||||
|
// OsExiter is the function used when the app exits. If not set defaults to os.Exit.
|
||||||
|
var OsExiter = os.Exit |
||||||
|
|
||||||
|
// ErrWriter is used to write errors to the user. This can be anything
|
||||||
|
// implementing the io.Writer interface and defaults to os.Stderr.
|
||||||
|
var ErrWriter io.Writer = os.Stderr |
||||||
|
|
||||||
|
// MultiError is an error that wraps multiple errors.
|
||||||
|
type MultiError struct { |
||||||
|
Errors []error |
||||||
|
} |
||||||
|
|
||||||
|
// NewMultiError creates a new MultiError. Pass in one or more errors.
|
||||||
|
func NewMultiError(err ...error) MultiError { |
||||||
|
return MultiError{Errors: err} |
||||||
|
} |
||||||
|
|
||||||
|
// Error implements the error interface.
|
||||||
|
func (m MultiError) Error() string { |
||||||
|
errs := make([]string, len(m.Errors)) |
||||||
|
for i, err := range m.Errors { |
||||||
|
errs[i] = err.Error() |
||||||
|
} |
||||||
|
|
||||||
|
return strings.Join(errs, "\n") |
||||||
|
} |
||||||
|
|
||||||
|
type ErrorFormatter interface { |
||||||
|
Format(s fmt.State, verb rune) |
||||||
|
} |
||||||
|
|
||||||
|
// ExitCoder is the interface checked by `App` and `Command` for a custom exit
|
||||||
|
// code
|
||||||
|
type ExitCoder interface { |
||||||
|
error |
||||||
|
ExitCode() int |
||||||
|
} |
||||||
|
|
||||||
|
// ExitError fulfills both the builtin `error` interface and `ExitCoder`
|
||||||
|
type ExitError struct { |
||||||
|
exitCode int |
||||||
|
message interface{} |
||||||
|
} |
||||||
|
|
||||||
|
// NewExitError makes a new *ExitError
|
||||||
|
func NewExitError(message interface{}, exitCode int) *ExitError { |
||||||
|
return &ExitError{ |
||||||
|
exitCode: exitCode, |
||||||
|
message: message, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Error returns the string message, fulfilling the interface required by
|
||||||
|
// `error`
|
||||||
|
func (ee *ExitError) Error() string { |
||||||
|
return fmt.Sprintf("%v", ee.message) |
||||||
|
} |
||||||
|
|
||||||
|
// ExitCode returns the exit code, fulfilling the interface required by
|
||||||
|
// `ExitCoder`
|
||||||
|
func (ee *ExitError) ExitCode() int { |
||||||
|
return ee.exitCode |
||||||
|
} |
||||||
|
|
||||||
|
// HandleExitCoder checks if the error fulfills the ExitCoder interface, and if
|
||||||
|
// so prints the error to stderr (if it is non-empty) and calls OsExiter with the
|
||||||
|
// given exit code. If the given error is a MultiError, then this func is
|
||||||
|
// called on all members of the Errors slice and calls OsExiter with the last exit code.
|
||||||
|
func HandleExitCoder(err error) { |
||||||
|
if err == nil { |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
if exitErr, ok := err.(ExitCoder); ok { |
||||||
|
if err.Error() != "" { |
||||||
|
if _, ok := exitErr.(ErrorFormatter); ok { |
||||||
|
fmt.Fprintf(ErrWriter, "%+v\n", err) |
||||||
|
} else { |
||||||
|
fmt.Fprintln(ErrWriter, err) |
||||||
|
} |
||||||
|
} |
||||||
|
OsExiter(exitErr.ExitCode()) |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
if multiErr, ok := err.(MultiError); ok { |
||||||
|
code := handleMultiError(multiErr) |
||||||
|
OsExiter(code) |
||||||
|
return |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func handleMultiError(multiErr MultiError) int { |
||||||
|
code := 1 |
||||||
|
for _, merr := range multiErr.Errors { |
||||||
|
if multiErr2, ok := merr.(MultiError); ok { |
||||||
|
code = handleMultiError(multiErr2) |
||||||
|
} else { |
||||||
|
fmt.Fprintln(ErrWriter, merr) |
||||||
|
if exitErr, ok := merr.(ExitCoder); ok { |
||||||
|
code = exitErr.ExitCode() |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return code |
||||||
|
} |
@ -0,0 +1,93 @@ |
|||||||
|
[ |
||||||
|
{ |
||||||
|
"name": "Bool", |
||||||
|
"type": "bool", |
||||||
|
"value": false, |
||||||
|
"context_default": "false", |
||||||
|
"parser": "strconv.ParseBool(f.Value.String())" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"name": "BoolT", |
||||||
|
"type": "bool", |
||||||
|
"value": false, |
||||||
|
"doctail": " that is true by default", |
||||||
|
"context_default": "false", |
||||||
|
"parser": "strconv.ParseBool(f.Value.String())" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"name": "Duration", |
||||||
|
"type": "time.Duration", |
||||||
|
"doctail": " (see https://golang.org/pkg/time/#ParseDuration)", |
||||||
|
"context_default": "0", |
||||||
|
"parser": "time.ParseDuration(f.Value.String())" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"name": "Float64", |
||||||
|
"type": "float64", |
||||||
|
"context_default": "0", |
||||||
|
"parser": "strconv.ParseFloat(f.Value.String(), 64)" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"name": "Generic", |
||||||
|
"type": "Generic", |
||||||
|
"dest": false, |
||||||
|
"context_default": "nil", |
||||||
|
"context_type": "interface{}" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"name": "Int64", |
||||||
|
"type": "int64", |
||||||
|
"context_default": "0", |
||||||
|
"parser": "strconv.ParseInt(f.Value.String(), 0, 64)" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"name": "Int", |
||||||
|
"type": "int", |
||||||
|
"context_default": "0", |
||||||
|
"parser": "strconv.ParseInt(f.Value.String(), 0, 64)", |
||||||
|
"parser_cast": "int(parsed)" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"name": "IntSlice", |
||||||
|
"type": "*IntSlice", |
||||||
|
"dest": false, |
||||||
|
"context_default": "nil", |
||||||
|
"context_type": "[]int", |
||||||
|
"parser": "(f.Value.(*IntSlice)).Value(), error(nil)" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"name": "Int64Slice", |
||||||
|
"type": "*Int64Slice", |
||||||
|
"dest": false, |
||||||
|
"context_default": "nil", |
||||||
|
"context_type": "[]int64", |
||||||
|
"parser": "(f.Value.(*Int64Slice)).Value(), error(nil)" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"name": "String", |
||||||
|
"type": "string", |
||||||
|
"context_default": "\"\"", |
||||||
|
"parser": "f.Value.String(), error(nil)" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"name": "StringSlice", |
||||||
|
"type": "*StringSlice", |
||||||
|
"dest": false, |
||||||
|
"context_default": "nil", |
||||||
|
"context_type": "[]string", |
||||||
|
"parser": "(f.Value.(*StringSlice)).Value(), error(nil)" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"name": "Uint64", |
||||||
|
"type": "uint64", |
||||||
|
"context_default": "0", |
||||||
|
"parser": "strconv.ParseUint(f.Value.String(), 0, 64)" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"name": "Uint", |
||||||
|
"type": "uint", |
||||||
|
"context_default": "0", |
||||||
|
"parser": "strconv.ParseUint(f.Value.String(), 0, 64)", |
||||||
|
"parser_cast": "uint(parsed)" |
||||||
|
} |
||||||
|
] |
@ -0,0 +1,799 @@ |
|||||||
|
package cli |
||||||
|
|
||||||
|
import ( |
||||||
|
"flag" |
||||||
|
"fmt" |
||||||
|
"reflect" |
||||||
|
"runtime" |
||||||
|
"strconv" |
||||||
|
"strings" |
||||||
|
"syscall" |
||||||
|
"time" |
||||||
|
) |
||||||
|
|
||||||
|
const defaultPlaceholder = "value" |
||||||
|
|
||||||
|
// BashCompletionFlag enables bash-completion for all commands and subcommands
|
||||||
|
var BashCompletionFlag Flag = BoolFlag{ |
||||||
|
Name: "generate-bash-completion", |
||||||
|
Hidden: true, |
||||||
|
} |
||||||
|
|
||||||
|
// VersionFlag prints the version for the application
|
||||||
|
var VersionFlag Flag = BoolFlag{ |
||||||
|
Name: "version, v", |
||||||
|
Usage: "print the version", |
||||||
|
} |
||||||
|
|
||||||
|
// HelpFlag prints the help for all commands and subcommands
|
||||||
|
// Set to the zero value (BoolFlag{}) to disable flag -- keeps subcommand
|
||||||
|
// unless HideHelp is set to true)
|
||||||
|
var HelpFlag Flag = BoolFlag{ |
||||||
|
Name: "help, h", |
||||||
|
Usage: "show help", |
||||||
|
} |
||||||
|
|
||||||
|
// FlagStringer converts a flag definition to a string. This is used by help
|
||||||
|
// to display a flag.
|
||||||
|
var FlagStringer FlagStringFunc = stringifyFlag |
||||||
|
|
||||||
|
// FlagsByName is a slice of Flag.
|
||||||
|
type FlagsByName []Flag |
||||||
|
|
||||||
|
func (f FlagsByName) Len() int { |
||||||
|
return len(f) |
||||||
|
} |
||||||
|
|
||||||
|
func (f FlagsByName) Less(i, j int) bool { |
||||||
|
return f[i].GetName() < f[j].GetName() |
||||||
|
} |
||||||
|
|
||||||
|
func (f FlagsByName) Swap(i, j int) { |
||||||
|
f[i], f[j] = f[j], f[i] |
||||||
|
} |
||||||
|
|
||||||
|
// Flag is a common interface related to parsing flags in cli.
|
||||||
|
// For more advanced flag parsing techniques, it is recommended that
|
||||||
|
// this interface be implemented.
|
||||||
|
type Flag interface { |
||||||
|
fmt.Stringer |
||||||
|
// Apply Flag settings to the given flag set
|
||||||
|
Apply(*flag.FlagSet) |
||||||
|
GetName() string |
||||||
|
} |
||||||
|
|
||||||
|
// errorableFlag is an interface that allows us to return errors during apply
|
||||||
|
// it allows flags defined in this library to return errors in a fashion backwards compatible
|
||||||
|
// TODO remove in v2 and modify the existing Flag interface to return errors
|
||||||
|
type errorableFlag interface { |
||||||
|
Flag |
||||||
|
|
||||||
|
ApplyWithError(*flag.FlagSet) error |
||||||
|
} |
||||||
|
|
||||||
|
func flagSet(name string, flags []Flag) (*flag.FlagSet, error) { |
||||||
|
set := flag.NewFlagSet(name, flag.ContinueOnError) |
||||||
|
|
||||||
|
for _, f := range flags { |
||||||
|
//TODO remove in v2 when errorableFlag is removed
|
||||||
|
if ef, ok := f.(errorableFlag); ok { |
||||||
|
if err := ef.ApplyWithError(set); err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
} else { |
||||||
|
f.Apply(set) |
||||||
|
} |
||||||
|
} |
||||||
|
return set, nil |
||||||
|
} |
||||||
|
|
||||||
|
func eachName(longName string, fn func(string)) { |
||||||
|
parts := strings.Split(longName, ",") |
||||||
|
for _, name := range parts { |
||||||
|
name = strings.Trim(name, " ") |
||||||
|
fn(name) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Generic is a generic parseable type identified by a specific flag
|
||||||
|
type Generic interface { |
||||||
|
Set(value string) error |
||||||
|
String() string |
||||||
|
} |
||||||
|
|
||||||
|
// Apply takes the flagset and calls Set on the generic flag with the value
|
||||||
|
// provided by the user for parsing by the flag
|
||||||
|
// Ignores parsing errors
|
||||||
|
func (f GenericFlag) Apply(set *flag.FlagSet) { |
||||||
|
f.ApplyWithError(set) |
||||||
|
} |
||||||
|
|
||||||
|
// ApplyWithError takes the flagset and calls Set on the generic flag with the value
|
||||||
|
// provided by the user for parsing by the flag
|
||||||
|
func (f GenericFlag) ApplyWithError(set *flag.FlagSet) error { |
||||||
|
val := f.Value |
||||||
|
if f.EnvVar != "" { |
||||||
|
for _, envVar := range strings.Split(f.EnvVar, ",") { |
||||||
|
envVar = strings.TrimSpace(envVar) |
||||||
|
if envVal, ok := syscall.Getenv(envVar); ok { |
||||||
|
if err := val.Set(envVal); err != nil { |
||||||
|
return fmt.Errorf("could not parse %s as value for flag %s: %s", envVal, f.Name, err) |
||||||
|
} |
||||||
|
break |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
eachName(f.Name, func(name string) { |
||||||
|
set.Var(f.Value, name, f.Usage) |
||||||
|
}) |
||||||
|
|
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// StringSlice is an opaque type for []string to satisfy flag.Value and flag.Getter
|
||||||
|
type StringSlice []string |
||||||
|
|
||||||
|
// Set appends the string value to the list of values
|
||||||
|
func (f *StringSlice) Set(value string) error { |
||||||
|
*f = append(*f, value) |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// String returns a readable representation of this value (for usage defaults)
|
||||||
|
func (f *StringSlice) String() string { |
||||||
|
return fmt.Sprintf("%s", *f) |
||||||
|
} |
||||||
|
|
||||||
|
// Value returns the slice of strings set by this flag
|
||||||
|
func (f *StringSlice) Value() []string { |
||||||
|
return *f |
||||||
|
} |
||||||
|
|
||||||
|
// Get returns the slice of strings set by this flag
|
||||||
|
func (f *StringSlice) Get() interface{} { |
||||||
|
return *f |
||||||
|
} |
||||||
|
|
||||||
|
// Apply populates the flag given the flag set and environment
|
||||||
|
// Ignores errors
|
||||||
|
func (f StringSliceFlag) Apply(set *flag.FlagSet) { |
||||||
|
f.ApplyWithError(set) |
||||||
|
} |
||||||
|
|
||||||
|
// ApplyWithError populates the flag given the flag set and environment
|
||||||
|
func (f StringSliceFlag) ApplyWithError(set *flag.FlagSet) error { |
||||||
|
if f.EnvVar != "" { |
||||||
|
for _, envVar := range strings.Split(f.EnvVar, ",") { |
||||||
|
envVar = strings.TrimSpace(envVar) |
||||||
|
if envVal, ok := syscall.Getenv(envVar); ok { |
||||||
|
newVal := &StringSlice{} |
||||||
|
for _, s := range strings.Split(envVal, ",") { |
||||||
|
s = strings.TrimSpace(s) |
||||||
|
if err := newVal.Set(s); err != nil { |
||||||
|
return fmt.Errorf("could not parse %s as string value for flag %s: %s", envVal, f.Name, err) |
||||||
|
} |
||||||
|
} |
||||||
|
f.Value = newVal |
||||||
|
break |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
eachName(f.Name, func(name string) { |
||||||
|
if f.Value == nil { |
||||||
|
f.Value = &StringSlice{} |
||||||
|
} |
||||||
|
set.Var(f.Value, name, f.Usage) |
||||||
|
}) |
||||||
|
|
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// IntSlice is an opaque type for []int to satisfy flag.Value and flag.Getter
|
||||||
|
type IntSlice []int |
||||||
|
|
||||||
|
// Set parses the value into an integer and appends it to the list of values
|
||||||
|
func (f *IntSlice) Set(value string) error { |
||||||
|
tmp, err := strconv.Atoi(value) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
*f = append(*f, tmp) |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// String returns a readable representation of this value (for usage defaults)
|
||||||
|
func (f *IntSlice) String() string { |
||||||
|
return fmt.Sprintf("%#v", *f) |
||||||
|
} |
||||||
|
|
||||||
|
// Value returns the slice of ints set by this flag
|
||||||
|
func (f *IntSlice) Value() []int { |
||||||
|
return *f |
||||||
|
} |
||||||
|
|
||||||
|
// Get returns the slice of ints set by this flag
|
||||||
|
func (f *IntSlice) Get() interface{} { |
||||||
|
return *f |
||||||
|
} |
||||||
|
|
||||||
|
// Apply populates the flag given the flag set and environment
|
||||||
|
// Ignores errors
|
||||||
|
func (f IntSliceFlag) Apply(set *flag.FlagSet) { |
||||||
|
f.ApplyWithError(set) |
||||||
|
} |
||||||
|
|
||||||
|
// ApplyWithError populates the flag given the flag set and environment
|
||||||
|
func (f IntSliceFlag) ApplyWithError(set *flag.FlagSet) error { |
||||||
|
if f.EnvVar != "" { |
||||||
|
for _, envVar := range strings.Split(f.EnvVar, ",") { |
||||||
|
envVar = strings.TrimSpace(envVar) |
||||||
|
if envVal, ok := syscall.Getenv(envVar); ok { |
||||||
|
newVal := &IntSlice{} |
||||||
|
for _, s := range strings.Split(envVal, ",") { |
||||||
|
s = strings.TrimSpace(s) |
||||||
|
if err := newVal.Set(s); err != nil { |
||||||
|
return fmt.Errorf("could not parse %s as int slice value for flag %s: %s", envVal, f.Name, err) |
||||||
|
} |
||||||
|
} |
||||||
|
f.Value = newVal |
||||||
|
break |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
eachName(f.Name, func(name string) { |
||||||
|
if f.Value == nil { |
||||||
|
f.Value = &IntSlice{} |
||||||
|
} |
||||||
|
set.Var(f.Value, name, f.Usage) |
||||||
|
}) |
||||||
|
|
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// Int64Slice is an opaque type for []int to satisfy flag.Value and flag.Getter
|
||||||
|
type Int64Slice []int64 |
||||||
|
|
||||||
|
// Set parses the value into an integer and appends it to the list of values
|
||||||
|
func (f *Int64Slice) Set(value string) error { |
||||||
|
tmp, err := strconv.ParseInt(value, 10, 64) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
*f = append(*f, tmp) |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// String returns a readable representation of this value (for usage defaults)
|
||||||
|
func (f *Int64Slice) String() string { |
||||||
|
return fmt.Sprintf("%#v", *f) |
||||||
|
} |
||||||
|
|
||||||
|
// Value returns the slice of ints set by this flag
|
||||||
|
func (f *Int64Slice) Value() []int64 { |
||||||
|
return *f |
||||||
|
} |
||||||
|
|
||||||
|
// Get returns the slice of ints set by this flag
|
||||||
|
func (f *Int64Slice) Get() interface{} { |
||||||
|
return *f |
||||||
|
} |
||||||
|
|
||||||
|
// Apply populates the flag given the flag set and environment
|
||||||
|
// Ignores errors
|
||||||
|
func (f Int64SliceFlag) Apply(set *flag.FlagSet) { |
||||||
|
f.ApplyWithError(set) |
||||||
|
} |
||||||
|
|
||||||
|
// ApplyWithError populates the flag given the flag set and environment
|
||||||
|
func (f Int64SliceFlag) ApplyWithError(set *flag.FlagSet) error { |
||||||
|
if f.EnvVar != "" { |
||||||
|
for _, envVar := range strings.Split(f.EnvVar, ",") { |
||||||
|
envVar = strings.TrimSpace(envVar) |
||||||
|
if envVal, ok := syscall.Getenv(envVar); ok { |
||||||
|
newVal := &Int64Slice{} |
||||||
|
for _, s := range strings.Split(envVal, ",") { |
||||||
|
s = strings.TrimSpace(s) |
||||||
|
if err := newVal.Set(s); err != nil { |
||||||
|
return fmt.Errorf("could not parse %s as int64 slice value for flag %s: %s", envVal, f.Name, err) |
||||||
|
} |
||||||
|
} |
||||||
|
f.Value = newVal |
||||||
|
break |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
eachName(f.Name, func(name string) { |
||||||
|
if f.Value == nil { |
||||||
|
f.Value = &Int64Slice{} |
||||||
|
} |
||||||
|
set.Var(f.Value, name, f.Usage) |
||||||
|
}) |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// Apply populates the flag given the flag set and environment
|
||||||
|
// Ignores errors
|
||||||
|
func (f BoolFlag) Apply(set *flag.FlagSet) { |
||||||
|
f.ApplyWithError(set) |
||||||
|
} |
||||||
|
|
||||||
|
// ApplyWithError populates the flag given the flag set and environment
|
||||||
|
func (f BoolFlag) ApplyWithError(set *flag.FlagSet) error { |
||||||
|
val := false |
||||||
|
if f.EnvVar != "" { |
||||||
|
for _, envVar := range strings.Split(f.EnvVar, ",") { |
||||||
|
envVar = strings.TrimSpace(envVar) |
||||||
|
if envVal, ok := syscall.Getenv(envVar); ok { |
||||||
|
if envVal == "" { |
||||||
|
val = false |
||||||
|
break |
||||||
|
} |
||||||
|
|
||||||
|
envValBool, err := strconv.ParseBool(envVal) |
||||||
|
if err != nil { |
||||||
|
return fmt.Errorf("could not parse %s as bool value for flag %s: %s", envVal, f.Name, err) |
||||||
|
} |
||||||
|
|
||||||
|
val = envValBool |
||||||
|
break |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
eachName(f.Name, func(name string) { |
||||||
|
if f.Destination != nil { |
||||||
|
set.BoolVar(f.Destination, name, val, f.Usage) |
||||||
|
return |
||||||
|
} |
||||||
|
set.Bool(name, val, f.Usage) |
||||||
|
}) |
||||||
|
|
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// Apply populates the flag given the flag set and environment
|
||||||
|
// Ignores errors
|
||||||
|
func (f BoolTFlag) Apply(set *flag.FlagSet) { |
||||||
|
f.ApplyWithError(set) |
||||||
|
} |
||||||
|
|
||||||
|
// ApplyWithError populates the flag given the flag set and environment
|
||||||
|
func (f BoolTFlag) ApplyWithError(set *flag.FlagSet) error { |
||||||
|
val := true |
||||||
|
if f.EnvVar != "" { |
||||||
|
for _, envVar := range strings.Split(f.EnvVar, ",") { |
||||||
|
envVar = strings.TrimSpace(envVar) |
||||||
|
if envVal, ok := syscall.Getenv(envVar); ok { |
||||||
|
if envVal == "" { |
||||||
|
val = false |
||||||
|
break |
||||||
|
} |
||||||
|
|
||||||
|
envValBool, err := strconv.ParseBool(envVal) |
||||||
|
if err != nil { |
||||||
|
return fmt.Errorf("could not parse %s as bool value for flag %s: %s", envVal, f.Name, err) |
||||||
|
} |
||||||
|
|
||||||
|
val = envValBool |
||||||
|
break |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
eachName(f.Name, func(name string) { |
||||||
|
if f.Destination != nil { |
||||||
|
set.BoolVar(f.Destination, name, val, f.Usage) |
||||||
|
return |
||||||
|
} |
||||||
|
set.Bool(name, val, f.Usage) |
||||||
|
}) |
||||||
|
|
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// Apply populates the flag given the flag set and environment
|
||||||
|
// Ignores errors
|
||||||
|
func (f StringFlag) Apply(set *flag.FlagSet) { |
||||||
|
f.ApplyWithError(set) |
||||||
|
} |
||||||
|
|
||||||
|
// ApplyWithError populates the flag given the flag set and environment
|
||||||
|
func (f StringFlag) ApplyWithError(set *flag.FlagSet) error { |
||||||
|
if f.EnvVar != "" { |
||||||
|
for _, envVar := range strings.Split(f.EnvVar, ",") { |
||||||
|
envVar = strings.TrimSpace(envVar) |
||||||
|
if envVal, ok := syscall.Getenv(envVar); ok { |
||||||
|
f.Value = envVal |
||||||
|
break |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
eachName(f.Name, func(name string) { |
||||||
|
if f.Destination != nil { |
||||||
|
set.StringVar(f.Destination, name, f.Value, f.Usage) |
||||||
|
return |
||||||
|
} |
||||||
|
set.String(name, f.Value, f.Usage) |
||||||
|
}) |
||||||
|
|
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// Apply populates the flag given the flag set and environment
|
||||||
|
// Ignores errors
|
||||||
|
func (f IntFlag) Apply(set *flag.FlagSet) { |
||||||
|
f.ApplyWithError(set) |
||||||
|
} |
||||||
|
|
||||||
|
// ApplyWithError populates the flag given the flag set and environment
|
||||||
|
func (f IntFlag) ApplyWithError(set *flag.FlagSet) error { |
||||||
|
if f.EnvVar != "" { |
||||||
|
for _, envVar := range strings.Split(f.EnvVar, ",") { |
||||||
|
envVar = strings.TrimSpace(envVar) |
||||||
|
if envVal, ok := syscall.Getenv(envVar); ok { |
||||||
|
envValInt, err := strconv.ParseInt(envVal, 0, 64) |
||||||
|
if err != nil { |
||||||
|
return fmt.Errorf("could not parse %s as int value for flag %s: %s", envVal, f.Name, err) |
||||||
|
} |
||||||
|
f.Value = int(envValInt) |
||||||
|
break |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
eachName(f.Name, func(name string) { |
||||||
|
if f.Destination != nil { |
||||||
|
set.IntVar(f.Destination, name, f.Value, f.Usage) |
||||||
|
return |
||||||
|
} |
||||||
|
set.Int(name, f.Value, f.Usage) |
||||||
|
}) |
||||||
|
|
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// Apply populates the flag given the flag set and environment
|
||||||
|
// Ignores errors
|
||||||
|
func (f Int64Flag) Apply(set *flag.FlagSet) { |
||||||
|
f.ApplyWithError(set) |
||||||
|
} |
||||||
|
|
||||||
|
// ApplyWithError populates the flag given the flag set and environment
|
||||||
|
func (f Int64Flag) ApplyWithError(set *flag.FlagSet) error { |
||||||
|
if f.EnvVar != "" { |
||||||
|
for _, envVar := range strings.Split(f.EnvVar, ",") { |
||||||
|
envVar = strings.TrimSpace(envVar) |
||||||
|
if envVal, ok := syscall.Getenv(envVar); ok { |
||||||
|
envValInt, err := strconv.ParseInt(envVal, 0, 64) |
||||||
|
if err != nil { |
||||||
|
return fmt.Errorf("could not parse %s as int value for flag %s: %s", envVal, f.Name, err) |
||||||
|
} |
||||||
|
|
||||||
|
f.Value = envValInt |
||||||
|
break |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
eachName(f.Name, func(name string) { |
||||||
|
if f.Destination != nil { |
||||||
|
set.Int64Var(f.Destination, name, f.Value, f.Usage) |
||||||
|
return |
||||||
|
} |
||||||
|
set.Int64(name, f.Value, f.Usage) |
||||||
|
}) |
||||||
|
|
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// Apply populates the flag given the flag set and environment
|
||||||
|
// Ignores errors
|
||||||
|
func (f UintFlag) Apply(set *flag.FlagSet) { |
||||||
|
f.ApplyWithError(set) |
||||||
|
} |
||||||
|
|
||||||
|
// ApplyWithError populates the flag given the flag set and environment
|
||||||
|
func (f UintFlag) ApplyWithError(set *flag.FlagSet) error { |
||||||
|
if f.EnvVar != "" { |
||||||
|
for _, envVar := range strings.Split(f.EnvVar, ",") { |
||||||
|
envVar = strings.TrimSpace(envVar) |
||||||
|
if envVal, ok := syscall.Getenv(envVar); ok { |
||||||
|
envValInt, err := strconv.ParseUint(envVal, 0, 64) |
||||||
|
if err != nil { |
||||||
|
return fmt.Errorf("could not parse %s as uint value for flag %s: %s", envVal, f.Name, err) |
||||||
|
} |
||||||
|
|
||||||
|
f.Value = uint(envValInt) |
||||||
|
break |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
eachName(f.Name, func(name string) { |
||||||
|
if f.Destination != nil { |
||||||
|
set.UintVar(f.Destination, name, f.Value, f.Usage) |
||||||
|
return |
||||||
|
} |
||||||
|
set.Uint(name, f.Value, f.Usage) |
||||||
|
}) |
||||||
|
|
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// Apply populates the flag given the flag set and environment
|
||||||
|
// Ignores errors
|
||||||
|
func (f Uint64Flag) Apply(set *flag.FlagSet) { |
||||||
|
f.ApplyWithError(set) |
||||||
|
} |
||||||
|
|
||||||
|
// ApplyWithError populates the flag given the flag set and environment
|
||||||
|
func (f Uint64Flag) ApplyWithError(set *flag.FlagSet) error { |
||||||
|
if f.EnvVar != "" { |
||||||
|
for _, envVar := range strings.Split(f.EnvVar, ",") { |
||||||
|
envVar = strings.TrimSpace(envVar) |
||||||
|
if envVal, ok := syscall.Getenv(envVar); ok { |
||||||
|
envValInt, err := strconv.ParseUint(envVal, 0, 64) |
||||||
|
if err != nil { |
||||||
|
return fmt.Errorf("could not parse %s as uint64 value for flag %s: %s", envVal, f.Name, err) |
||||||
|
} |
||||||
|
|
||||||
|
f.Value = uint64(envValInt) |
||||||
|
break |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
eachName(f.Name, func(name string) { |
||||||
|
if f.Destination != nil { |
||||||
|
set.Uint64Var(f.Destination, name, f.Value, f.Usage) |
||||||
|
return |
||||||
|
} |
||||||
|
set.Uint64(name, f.Value, f.Usage) |
||||||
|
}) |
||||||
|
|
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// Apply populates the flag given the flag set and environment
|
||||||
|
// Ignores errors
|
||||||
|
func (f DurationFlag) Apply(set *flag.FlagSet) { |
||||||
|
f.ApplyWithError(set) |
||||||
|
} |
||||||
|
|
||||||
|
// ApplyWithError populates the flag given the flag set and environment
|
||||||
|
func (f DurationFlag) ApplyWithError(set *flag.FlagSet) error { |
||||||
|
if f.EnvVar != "" { |
||||||
|
for _, envVar := range strings.Split(f.EnvVar, ",") { |
||||||
|
envVar = strings.TrimSpace(envVar) |
||||||
|
if envVal, ok := syscall.Getenv(envVar); ok { |
||||||
|
envValDuration, err := time.ParseDuration(envVal) |
||||||
|
if err != nil { |
||||||
|
return fmt.Errorf("could not parse %s as duration for flag %s: %s", envVal, f.Name, err) |
||||||
|
} |
||||||
|
|
||||||
|
f.Value = envValDuration |
||||||
|
break |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
eachName(f.Name, func(name string) { |
||||||
|
if f.Destination != nil { |
||||||
|
set.DurationVar(f.Destination, name, f.Value, f.Usage) |
||||||
|
return |
||||||
|
} |
||||||
|
set.Duration(name, f.Value, f.Usage) |
||||||
|
}) |
||||||
|
|
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// Apply populates the flag given the flag set and environment
|
||||||
|
// Ignores errors
|
||||||
|
func (f Float64Flag) Apply(set *flag.FlagSet) { |
||||||
|
f.ApplyWithError(set) |
||||||
|
} |
||||||
|
|
||||||
|
// ApplyWithError populates the flag given the flag set and environment
|
||||||
|
func (f Float64Flag) ApplyWithError(set *flag.FlagSet) error { |
||||||
|
if f.EnvVar != "" { |
||||||
|
for _, envVar := range strings.Split(f.EnvVar, ",") { |
||||||
|
envVar = strings.TrimSpace(envVar) |
||||||
|
if envVal, ok := syscall.Getenv(envVar); ok { |
||||||
|
envValFloat, err := strconv.ParseFloat(envVal, 10) |
||||||
|
if err != nil { |
||||||
|
return fmt.Errorf("could not parse %s as float64 value for flag %s: %s", envVal, f.Name, err) |
||||||
|
} |
||||||
|
|
||||||
|
f.Value = float64(envValFloat) |
||||||
|
break |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
eachName(f.Name, func(name string) { |
||||||
|
if f.Destination != nil { |
||||||
|
set.Float64Var(f.Destination, name, f.Value, f.Usage) |
||||||
|
return |
||||||
|
} |
||||||
|
set.Float64(name, f.Value, f.Usage) |
||||||
|
}) |
||||||
|
|
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
func visibleFlags(fl []Flag) []Flag { |
||||||
|
visible := []Flag{} |
||||||
|
for _, flag := range fl { |
||||||
|
field := flagValue(flag).FieldByName("Hidden") |
||||||
|
if !field.IsValid() || !field.Bool() { |
||||||
|
visible = append(visible, flag) |
||||||
|
} |
||||||
|
} |
||||||
|
return visible |
||||||
|
} |
||||||
|
|
||||||
|
func prefixFor(name string) (prefix string) { |
||||||
|
if len(name) == 1 { |
||||||
|
prefix = "-" |
||||||
|
} else { |
||||||
|
prefix = "--" |
||||||
|
} |
||||||
|
|
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
// Returns the placeholder, if any, and the unquoted usage string.
|
||||||
|
func unquoteUsage(usage string) (string, string) { |
||||||
|
for i := 0; i < len(usage); i++ { |
||||||
|
if usage[i] == '`' { |
||||||
|
for j := i + 1; j < len(usage); j++ { |
||||||
|
if usage[j] == '`' { |
||||||
|
name := usage[i+1 : j] |
||||||
|
usage = usage[:i] + name + usage[j+1:] |
||||||
|
return name, usage |
||||||
|
} |
||||||
|
} |
||||||
|
break |
||||||
|
} |
||||||
|
} |
||||||
|
return "", usage |
||||||
|
} |
||||||
|
|
||||||
|
func prefixedNames(fullName, placeholder string) string { |
||||||
|
var prefixed string |
||||||
|
parts := strings.Split(fullName, ",") |
||||||
|
for i, name := range parts { |
||||||
|
name = strings.Trim(name, " ") |
||||||
|
prefixed += prefixFor(name) + name |
||||||
|
if placeholder != "" { |
||||||
|
prefixed += " " + placeholder |
||||||
|
} |
||||||
|
if i < len(parts)-1 { |
||||||
|
prefixed += ", " |
||||||
|
} |
||||||
|
} |
||||||
|
return prefixed |
||||||
|
} |
||||||
|
|
||||||
|
func withEnvHint(envVar, str string) string { |
||||||
|
envText := "" |
||||||
|
if envVar != "" { |
||||||
|
prefix := "$" |
||||||
|
suffix := "" |
||||||
|
sep := ", $" |
||||||
|
if runtime.GOOS == "windows" { |
||||||
|
prefix = "%" |
||||||
|
suffix = "%" |
||||||
|
sep = "%, %" |
||||||
|
} |
||||||
|
envText = fmt.Sprintf(" [%s%s%s]", prefix, strings.Join(strings.Split(envVar, ","), sep), suffix) |
||||||
|
} |
||||||
|
return str + envText |
||||||
|
} |
||||||
|
|
||||||
|
func flagValue(f Flag) reflect.Value { |
||||||
|
fv := reflect.ValueOf(f) |
||||||
|
for fv.Kind() == reflect.Ptr { |
||||||
|
fv = reflect.Indirect(fv) |
||||||
|
} |
||||||
|
return fv |
||||||
|
} |
||||||
|
|
||||||
|
func stringifyFlag(f Flag) string { |
||||||
|
fv := flagValue(f) |
||||||
|
|
||||||
|
switch f.(type) { |
||||||
|
case IntSliceFlag: |
||||||
|
return withEnvHint(fv.FieldByName("EnvVar").String(), |
||||||
|
stringifyIntSliceFlag(f.(IntSliceFlag))) |
||||||
|
case Int64SliceFlag: |
||||||
|
return withEnvHint(fv.FieldByName("EnvVar").String(), |
||||||
|
stringifyInt64SliceFlag(f.(Int64SliceFlag))) |
||||||
|
case StringSliceFlag: |
||||||
|
return withEnvHint(fv.FieldByName("EnvVar").String(), |
||||||
|
stringifyStringSliceFlag(f.(StringSliceFlag))) |
||||||
|
} |
||||||
|
|
||||||
|
placeholder, usage := unquoteUsage(fv.FieldByName("Usage").String()) |
||||||
|
|
||||||
|
needsPlaceholder := false |
||||||
|
defaultValueString := "" |
||||||
|
|
||||||
|
if val := fv.FieldByName("Value"); val.IsValid() { |
||||||
|
needsPlaceholder = true |
||||||
|
defaultValueString = fmt.Sprintf(" (default: %v)", val.Interface()) |
||||||
|
|
||||||
|
if val.Kind() == reflect.String && val.String() != "" { |
||||||
|
defaultValueString = fmt.Sprintf(" (default: %q)", val.String()) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if defaultValueString == " (default: )" { |
||||||
|
defaultValueString = "" |
||||||
|
} |
||||||
|
|
||||||
|
if needsPlaceholder && placeholder == "" { |
||||||
|
placeholder = defaultPlaceholder |
||||||
|
} |
||||||
|
|
||||||
|
usageWithDefault := strings.TrimSpace(fmt.Sprintf("%s%s", usage, defaultValueString)) |
||||||
|
|
||||||
|
return withEnvHint(fv.FieldByName("EnvVar").String(), |
||||||
|
fmt.Sprintf("%s\t%s", prefixedNames(fv.FieldByName("Name").String(), placeholder), usageWithDefault)) |
||||||
|
} |
||||||
|
|
||||||
|
func stringifyIntSliceFlag(f IntSliceFlag) string { |
||||||
|
defaultVals := []string{} |
||||||
|
if f.Value != nil && len(f.Value.Value()) > 0 { |
||||||
|
for _, i := range f.Value.Value() { |
||||||
|
defaultVals = append(defaultVals, fmt.Sprintf("%d", i)) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return stringifySliceFlag(f.Usage, f.Name, defaultVals) |
||||||
|
} |
||||||
|
|
||||||
|
func stringifyInt64SliceFlag(f Int64SliceFlag) string { |
||||||
|
defaultVals := []string{} |
||||||
|
if f.Value != nil && len(f.Value.Value()) > 0 { |
||||||
|
for _, i := range f.Value.Value() { |
||||||
|
defaultVals = append(defaultVals, fmt.Sprintf("%d", i)) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return stringifySliceFlag(f.Usage, f.Name, defaultVals) |
||||||
|
} |
||||||
|
|
||||||
|
func stringifyStringSliceFlag(f StringSliceFlag) string { |
||||||
|
defaultVals := []string{} |
||||||
|
if f.Value != nil && len(f.Value.Value()) > 0 { |
||||||
|
for _, s := range f.Value.Value() { |
||||||
|
if len(s) > 0 { |
||||||
|
defaultVals = append(defaultVals, fmt.Sprintf("%q", s)) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return stringifySliceFlag(f.Usage, f.Name, defaultVals) |
||||||
|
} |
||||||
|
|
||||||
|
func stringifySliceFlag(usage, name string, defaultVals []string) string { |
||||||
|
placeholder, usage := unquoteUsage(usage) |
||||||
|
if placeholder == "" { |
||||||
|
placeholder = defaultPlaceholder |
||||||
|
} |
||||||
|
|
||||||
|
defaultVal := "" |
||||||
|
if len(defaultVals) > 0 { |
||||||
|
defaultVal = fmt.Sprintf(" (default: %s)", strings.Join(defaultVals, ", ")) |
||||||
|
} |
||||||
|
|
||||||
|
usageWithDefault := strings.TrimSpace(fmt.Sprintf("%s%s", usage, defaultVal)) |
||||||
|
return fmt.Sprintf("%s\t%s", prefixedNames(name, placeholder), usageWithDefault) |
||||||
|
} |
@ -0,0 +1,627 @@ |
|||||||
|
package cli |
||||||
|
|
||||||
|
import ( |
||||||
|
"flag" |
||||||
|
"strconv" |
||||||
|
"time" |
||||||
|
) |
||||||
|
|
||||||
|
// WARNING: This file is generated!
|
||||||
|
|
||||||
|
// BoolFlag is a flag with type bool
|
||||||
|
type BoolFlag struct { |
||||||
|
Name string |
||||||
|
Usage string |
||||||
|
EnvVar string |
||||||
|
Hidden bool |
||||||
|
Destination *bool |
||||||
|
} |
||||||
|
|
||||||
|
// String returns a readable representation of this value
|
||||||
|
// (for usage defaults)
|
||||||
|
func (f BoolFlag) String() string { |
||||||
|
return FlagStringer(f) |
||||||
|
} |
||||||
|
|
||||||
|
// GetName returns the name of the flag
|
||||||
|
func (f BoolFlag) GetName() string { |
||||||
|
return f.Name |
||||||
|
} |
||||||
|
|
||||||
|
// Bool looks up the value of a local BoolFlag, returns
|
||||||
|
// false if not found
|
||||||
|
func (c *Context) Bool(name string) bool { |
||||||
|
return lookupBool(name, c.flagSet) |
||||||
|
} |
||||||
|
|
||||||
|
// GlobalBool looks up the value of a global BoolFlag, returns
|
||||||
|
// false if not found
|
||||||
|
func (c *Context) GlobalBool(name string) bool { |
||||||
|
if fs := lookupGlobalFlagSet(name, c); fs != nil { |
||||||
|
return lookupBool(name, fs) |
||||||
|
} |
||||||
|
return false |
||||||
|
} |
||||||
|
|
||||||
|
func lookupBool(name string, set *flag.FlagSet) bool { |
||||||
|
f := set.Lookup(name) |
||||||
|
if f != nil { |
||||||
|
parsed, err := strconv.ParseBool(f.Value.String()) |
||||||
|
if err != nil { |
||||||
|
return false |
||||||
|
} |
||||||
|
return parsed |
||||||
|
} |
||||||
|
return false |
||||||
|
} |
||||||
|
|
||||||
|
// BoolTFlag is a flag with type bool that is true by default
|
||||||
|
type BoolTFlag struct { |
||||||
|
Name string |
||||||
|
Usage string |
||||||
|
EnvVar string |
||||||
|
Hidden bool |
||||||
|
Destination *bool |
||||||
|
} |
||||||
|
|
||||||
|
// String returns a readable representation of this value
|
||||||
|
// (for usage defaults)
|
||||||
|
func (f BoolTFlag) String() string { |
||||||
|
return FlagStringer(f) |
||||||
|
} |
||||||
|
|
||||||
|
// GetName returns the name of the flag
|
||||||
|
func (f BoolTFlag) GetName() string { |
||||||
|
return f.Name |
||||||
|
} |
||||||
|
|
||||||
|
// BoolT looks up the value of a local BoolTFlag, returns
|
||||||
|
// false if not found
|
||||||
|
func (c *Context) BoolT(name string) bool { |
||||||
|
return lookupBoolT(name, c.flagSet) |
||||||
|
} |
||||||
|
|
||||||
|
// GlobalBoolT looks up the value of a global BoolTFlag, returns
|
||||||
|
// false if not found
|
||||||
|
func (c *Context) GlobalBoolT(name string) bool { |
||||||
|
if fs := lookupGlobalFlagSet(name, c); fs != nil { |
||||||
|
return lookupBoolT(name, fs) |
||||||
|
} |
||||||
|
return false |
||||||
|
} |
||||||
|
|
||||||
|
func lookupBoolT(name string, set *flag.FlagSet) bool { |
||||||
|
f := set.Lookup(name) |
||||||
|
if f != nil { |
||||||
|
parsed, err := strconv.ParseBool(f.Value.String()) |
||||||
|
if err != nil { |
||||||
|
return false |
||||||
|
} |
||||||
|
return parsed |
||||||
|
} |
||||||
|
return false |
||||||
|
} |
||||||
|
|
||||||
|
// DurationFlag is a flag with type time.Duration (see https://golang.org/pkg/time/#ParseDuration)
|
||||||
|
type DurationFlag struct { |
||||||
|
Name string |
||||||
|
Usage string |
||||||
|
EnvVar string |
||||||
|
Hidden bool |
||||||
|
Value time.Duration |
||||||
|
Destination *time.Duration |
||||||
|
} |
||||||
|
|
||||||
|
// String returns a readable representation of this value
|
||||||
|
// (for usage defaults)
|
||||||
|
func (f DurationFlag) String() string { |
||||||
|
return FlagStringer(f) |
||||||
|
} |
||||||
|
|
||||||
|
// GetName returns the name of the flag
|
||||||
|
func (f DurationFlag) GetName() string { |
||||||
|
return f.Name |
||||||
|
} |
||||||
|
|
||||||
|
// Duration looks up the value of a local DurationFlag, returns
|
||||||
|
// 0 if not found
|
||||||
|
func (c *Context) Duration(name string) time.Duration { |
||||||
|
return lookupDuration(name, c.flagSet) |
||||||
|
} |
||||||
|
|
||||||
|
// GlobalDuration looks up the value of a global DurationFlag, returns
|
||||||
|
// 0 if not found
|
||||||
|
func (c *Context) GlobalDuration(name string) time.Duration { |
||||||
|
if fs := lookupGlobalFlagSet(name, c); fs != nil { |
||||||
|
return lookupDuration(name, fs) |
||||||
|
} |
||||||
|
return 0 |
||||||
|
} |
||||||
|
|
||||||
|
func lookupDuration(name string, set *flag.FlagSet) time.Duration { |
||||||
|
f := set.Lookup(name) |
||||||
|
if f != nil { |
||||||
|
parsed, err := time.ParseDuration(f.Value.String()) |
||||||
|
if err != nil { |
||||||
|
return 0 |
||||||
|
} |
||||||
|
return parsed |
||||||
|
} |
||||||
|
return 0 |
||||||
|
} |
||||||
|
|
||||||
|
// Float64Flag is a flag with type float64
|
||||||
|
type Float64Flag struct { |
||||||
|
Name string |
||||||
|
Usage string |
||||||
|
EnvVar string |
||||||
|
Hidden bool |
||||||
|
Value float64 |
||||||
|
Destination *float64 |
||||||
|
} |
||||||
|
|
||||||
|
// String returns a readable representation of this value
|
||||||
|
// (for usage defaults)
|
||||||
|
func (f Float64Flag) String() string { |
||||||
|
return FlagStringer(f) |
||||||
|
} |
||||||
|
|
||||||
|
// GetName returns the name of the flag
|
||||||
|
func (f Float64Flag) GetName() string { |
||||||
|
return f.Name |
||||||
|
} |
||||||
|
|
||||||
|
// Float64 looks up the value of a local Float64Flag, returns
|
||||||
|
// 0 if not found
|
||||||
|
func (c *Context) Float64(name string) float64 { |
||||||
|
return lookupFloat64(name, c.flagSet) |
||||||
|
} |
||||||
|
|
||||||
|
// GlobalFloat64 looks up the value of a global Float64Flag, returns
|
||||||
|
// 0 if not found
|
||||||
|
func (c *Context) GlobalFloat64(name string) float64 { |
||||||
|
if fs := lookupGlobalFlagSet(name, c); fs != nil { |
||||||
|
return lookupFloat64(name, fs) |
||||||
|
} |
||||||
|
return 0 |
||||||
|
} |
||||||
|
|
||||||
|
func lookupFloat64(name string, set *flag.FlagSet) float64 { |
||||||
|
f := set.Lookup(name) |
||||||
|
if f != nil { |
||||||
|
parsed, err := strconv.ParseFloat(f.Value.String(), 64) |
||||||
|
if err != nil { |
||||||
|
return 0 |
||||||
|
} |
||||||
|
return parsed |
||||||
|
} |
||||||
|
return 0 |
||||||
|
} |
||||||
|
|
||||||
|
// GenericFlag is a flag with type Generic
|
||||||
|
type GenericFlag struct { |
||||||
|
Name string |
||||||
|
Usage string |
||||||
|
EnvVar string |
||||||
|
Hidden bool |
||||||
|
Value Generic |
||||||
|
} |
||||||
|
|
||||||
|
// String returns a readable representation of this value
|
||||||
|
// (for usage defaults)
|
||||||
|
func (f GenericFlag) String() string { |
||||||
|
return FlagStringer(f) |
||||||
|
} |
||||||
|
|
||||||
|
// GetName returns the name of the flag
|
||||||
|
func (f GenericFlag) GetName() string { |
||||||
|
return f.Name |
||||||
|
} |
||||||
|
|
||||||
|
// Generic looks up the value of a local GenericFlag, returns
|
||||||
|
// nil if not found
|
||||||
|
func (c *Context) Generic(name string) interface{} { |
||||||
|
return lookupGeneric(name, c.flagSet) |
||||||
|
} |
||||||
|
|
||||||
|
// GlobalGeneric looks up the value of a global GenericFlag, returns
|
||||||
|
// nil if not found
|
||||||
|
func (c *Context) GlobalGeneric(name string) interface{} { |
||||||
|
if fs := lookupGlobalFlagSet(name, c); fs != nil { |
||||||
|
return lookupGeneric(name, fs) |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
func lookupGeneric(name string, set *flag.FlagSet) interface{} { |
||||||
|
f := set.Lookup(name) |
||||||
|
if f != nil { |
||||||
|
parsed, err := f.Value, error(nil) |
||||||
|
if err != nil { |
||||||
|
return nil |
||||||
|
} |
||||||
|
return parsed |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// Int64Flag is a flag with type int64
|
||||||
|
type Int64Flag struct { |
||||||
|
Name string |
||||||
|
Usage string |
||||||
|
EnvVar string |
||||||
|
Hidden bool |
||||||
|
Value int64 |
||||||
|
Destination *int64 |
||||||
|
} |
||||||
|
|
||||||
|
// String returns a readable representation of this value
|
||||||
|
// (for usage defaults)
|
||||||
|
func (f Int64Flag) String() string { |
||||||
|
return FlagStringer(f) |
||||||
|
} |
||||||
|
|
||||||
|
// GetName returns the name of the flag
|
||||||
|
func (f Int64Flag) GetName() string { |
||||||
|
return f.Name |
||||||
|
} |
||||||
|
|
||||||
|
// Int64 looks up the value of a local Int64Flag, returns
|
||||||
|
// 0 if not found
|
||||||
|
func (c *Context) Int64(name string) int64 { |
||||||
|
return lookupInt64(name, c.flagSet) |
||||||
|
} |
||||||
|
|
||||||
|
// GlobalInt64 looks up the value of a global Int64Flag, returns
|
||||||
|
// 0 if not found
|
||||||
|
func (c *Context) GlobalInt64(name string) int64 { |
||||||
|
if fs := lookupGlobalFlagSet(name, c); fs != nil { |
||||||
|
return lookupInt64(name, fs) |
||||||
|
} |
||||||
|
return 0 |
||||||
|
} |
||||||
|
|
||||||
|
func lookupInt64(name string, set *flag.FlagSet) int64 { |
||||||
|
f := set.Lookup(name) |
||||||
|
if f != nil { |
||||||
|
parsed, err := strconv.ParseInt(f.Value.String(), 0, 64) |
||||||
|
if err != nil { |
||||||
|
return 0 |
||||||
|
} |
||||||
|
return parsed |
||||||
|
} |
||||||
|
return 0 |
||||||
|
} |
||||||
|
|
||||||
|
// IntFlag is a flag with type int
|
||||||
|
type IntFlag struct { |
||||||
|
Name string |
||||||
|
Usage string |
||||||
|
EnvVar string |
||||||
|
Hidden bool |
||||||
|
Value int |
||||||
|
Destination *int |
||||||
|
} |
||||||
|
|
||||||
|
// String returns a readable representation of this value
|
||||||
|
// (for usage defaults)
|
||||||
|
func (f IntFlag) String() string { |
||||||
|
return FlagStringer(f) |
||||||
|
} |
||||||
|
|
||||||
|
// GetName returns the name of the flag
|
||||||
|
func (f IntFlag) GetName() string { |
||||||
|
return f.Name |
||||||
|
} |
||||||
|
|
||||||
|
// Int looks up the value of a local IntFlag, returns
|
||||||
|
// 0 if not found
|
||||||
|
func (c *Context) Int(name string) int { |
||||||
|
return lookupInt(name, c.flagSet) |
||||||
|
} |
||||||
|
|
||||||
|
// GlobalInt looks up the value of a global IntFlag, returns
|
||||||
|
// 0 if not found
|
||||||
|
func (c *Context) GlobalInt(name string) int { |
||||||
|
if fs := lookupGlobalFlagSet(name, c); fs != nil { |
||||||
|
return lookupInt(name, fs) |
||||||
|
} |
||||||
|
return 0 |
||||||
|
} |
||||||
|
|
||||||
|
func lookupInt(name string, set *flag.FlagSet) int { |
||||||
|
f := set.Lookup(name) |
||||||
|
if f != nil { |
||||||
|
parsed, err := strconv.ParseInt(f.Value.String(), 0, 64) |
||||||
|
if err != nil { |
||||||
|
return 0 |
||||||
|
} |
||||||
|
return int(parsed) |
||||||
|
} |
||||||
|
return 0 |
||||||
|
} |
||||||
|
|
||||||
|
// IntSliceFlag is a flag with type *IntSlice
|
||||||
|
type IntSliceFlag struct { |
||||||
|
Name string |
||||||
|
Usage string |
||||||
|
EnvVar string |
||||||
|
Hidden bool |
||||||
|
Value *IntSlice |
||||||
|
} |
||||||
|
|
||||||
|
// String returns a readable representation of this value
|
||||||
|
// (for usage defaults)
|
||||||
|
func (f IntSliceFlag) String() string { |
||||||
|
return FlagStringer(f) |
||||||
|
} |
||||||
|
|
||||||
|
// GetName returns the name of the flag
|
||||||
|
func (f IntSliceFlag) GetName() string { |
||||||
|
return f.Name |
||||||
|
} |
||||||
|
|
||||||
|
// IntSlice looks up the value of a local IntSliceFlag, returns
|
||||||
|
// nil if not found
|
||||||
|
func (c *Context) IntSlice(name string) []int { |
||||||
|
return lookupIntSlice(name, c.flagSet) |
||||||
|
} |
||||||
|
|
||||||
|
// GlobalIntSlice looks up the value of a global IntSliceFlag, returns
|
||||||
|
// nil if not found
|
||||||
|
func (c *Context) GlobalIntSlice(name string) []int { |
||||||
|
if fs := lookupGlobalFlagSet(name, c); fs != nil { |
||||||
|
return lookupIntSlice(name, fs) |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
func lookupIntSlice(name string, set *flag.FlagSet) []int { |
||||||
|
f := set.Lookup(name) |
||||||
|
if f != nil { |
||||||
|
parsed, err := (f.Value.(*IntSlice)).Value(), error(nil) |
||||||
|
if err != nil { |
||||||
|
return nil |
||||||
|
} |
||||||
|
return parsed |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// Int64SliceFlag is a flag with type *Int64Slice
|
||||||
|
type Int64SliceFlag struct { |
||||||
|
Name string |
||||||
|
Usage string |
||||||
|
EnvVar string |
||||||
|
Hidden bool |
||||||
|
Value *Int64Slice |
||||||
|
} |
||||||
|
|
||||||
|
// String returns a readable representation of this value
|
||||||
|
// (for usage defaults)
|
||||||
|
func (f Int64SliceFlag) String() string { |
||||||
|
return FlagStringer(f) |
||||||
|
} |
||||||
|
|
||||||
|
// GetName returns the name of the flag
|
||||||
|
func (f Int64SliceFlag) GetName() string { |
||||||
|
return f.Name |
||||||
|
} |
||||||
|
|
||||||
|
// Int64Slice looks up the value of a local Int64SliceFlag, returns
|
||||||
|
// nil if not found
|
||||||
|
func (c *Context) Int64Slice(name string) []int64 { |
||||||
|
return lookupInt64Slice(name, c.flagSet) |
||||||
|
} |
||||||
|
|
||||||
|
// GlobalInt64Slice looks up the value of a global Int64SliceFlag, returns
|
||||||
|
// nil if not found
|
||||||
|
func (c *Context) GlobalInt64Slice(name string) []int64 { |
||||||
|
if fs := lookupGlobalFlagSet(name, c); fs != nil { |
||||||
|
return lookupInt64Slice(name, fs) |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
func lookupInt64Slice(name string, set *flag.FlagSet) []int64 { |
||||||
|
f := set.Lookup(name) |
||||||
|
if f != nil { |
||||||
|
parsed, err := (f.Value.(*Int64Slice)).Value(), error(nil) |
||||||
|
if err != nil { |
||||||
|
return nil |
||||||
|
} |
||||||
|
return parsed |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// StringFlag is a flag with type string
|
||||||
|
type StringFlag struct { |
||||||
|
Name string |
||||||
|
Usage string |
||||||
|
EnvVar string |
||||||
|
Hidden bool |
||||||
|
Value string |
||||||
|
Destination *string |
||||||
|
} |
||||||
|
|
||||||
|
// String returns a readable representation of this value
|
||||||
|
// (for usage defaults)
|
||||||
|
func (f StringFlag) String() string { |
||||||
|
return FlagStringer(f) |
||||||
|
} |
||||||
|
|
||||||
|
// GetName returns the name of the flag
|
||||||
|
func (f StringFlag) GetName() string { |
||||||
|
return f.Name |
||||||
|
} |
||||||
|
|
||||||
|
// String looks up the value of a local StringFlag, returns
|
||||||
|
// "" if not found
|
||||||
|
func (c *Context) String(name string) string { |
||||||
|
return lookupString(name, c.flagSet) |
||||||
|
} |
||||||
|
|
||||||
|
// GlobalString looks up the value of a global StringFlag, returns
|
||||||
|
// "" if not found
|
||||||
|
func (c *Context) GlobalString(name string) string { |
||||||
|
if fs := lookupGlobalFlagSet(name, c); fs != nil { |
||||||
|
return lookupString(name, fs) |
||||||
|
} |
||||||
|
return "" |
||||||
|
} |
||||||
|
|
||||||
|
func lookupString(name string, set *flag.FlagSet) string { |
||||||
|
f := set.Lookup(name) |
||||||
|
if f != nil { |
||||||
|
parsed, err := f.Value.String(), error(nil) |
||||||
|
if err != nil { |
||||||
|
return "" |
||||||
|
} |
||||||
|
return parsed |
||||||
|
} |
||||||
|
return "" |
||||||
|
} |
||||||
|
|
||||||
|
// StringSliceFlag is a flag with type *StringSlice
|
||||||
|
type StringSliceFlag struct { |
||||||
|
Name string |
||||||
|
Usage string |
||||||
|
EnvVar string |
||||||
|
Hidden bool |
||||||
|
Value *StringSlice |
||||||
|
} |
||||||
|
|
||||||
|
// String returns a readable representation of this value
|
||||||
|
// (for usage defaults)
|
||||||
|
func (f StringSliceFlag) String() string { |
||||||
|
return FlagStringer(f) |
||||||
|
} |
||||||
|
|
||||||
|
// GetName returns the name of the flag
|
||||||
|
func (f StringSliceFlag) GetName() string { |
||||||
|
return f.Name |
||||||
|
} |
||||||
|
|
||||||
|
// StringSlice looks up the value of a local StringSliceFlag, returns
|
||||||
|
// nil if not found
|
||||||
|
func (c *Context) StringSlice(name string) []string { |
||||||
|
return lookupStringSlice(name, c.flagSet) |
||||||
|
} |
||||||
|
|
||||||
|
// GlobalStringSlice looks up the value of a global StringSliceFlag, returns
|
||||||
|
// nil if not found
|
||||||
|
func (c *Context) GlobalStringSlice(name string) []string { |
||||||
|
if fs := lookupGlobalFlagSet(name, c); fs != nil { |
||||||
|
return lookupStringSlice(name, fs) |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
func lookupStringSlice(name string, set *flag.FlagSet) []string { |
||||||
|
f := set.Lookup(name) |
||||||
|
if f != nil { |
||||||
|
parsed, err := (f.Value.(*StringSlice)).Value(), error(nil) |
||||||
|
if err != nil { |
||||||
|
return nil |
||||||
|
} |
||||||
|
return parsed |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// Uint64Flag is a flag with type uint64
|
||||||
|
type Uint64Flag struct { |
||||||
|
Name string |
||||||
|
Usage string |
||||||
|
EnvVar string |
||||||
|
Hidden bool |
||||||
|
Value uint64 |
||||||
|
Destination *uint64 |
||||||
|
} |
||||||
|
|
||||||
|
// String returns a readable representation of this value
|
||||||
|
// (for usage defaults)
|
||||||
|
func (f Uint64Flag) String() string { |
||||||
|
return FlagStringer(f) |
||||||
|
} |
||||||
|
|
||||||
|
// GetName returns the name of the flag
|
||||||
|
func (f Uint64Flag) GetName() string { |
||||||
|
return f.Name |
||||||
|
} |
||||||
|
|
||||||
|
// Uint64 looks up the value of a local Uint64Flag, returns
|
||||||
|
// 0 if not found
|
||||||
|
func (c *Context) Uint64(name string) uint64 { |
||||||
|
return lookupUint64(name, c.flagSet) |
||||||
|
} |
||||||
|
|
||||||
|
// GlobalUint64 looks up the value of a global Uint64Flag, returns
|
||||||
|
// 0 if not found
|
||||||
|
func (c *Context) GlobalUint64(name string) uint64 { |
||||||
|
if fs := lookupGlobalFlagSet(name, c); fs != nil { |
||||||
|
return lookupUint64(name, fs) |
||||||
|
} |
||||||
|
return 0 |
||||||
|
} |
||||||
|
|
||||||
|
func lookupUint64(name string, set *flag.FlagSet) uint64 { |
||||||
|
f := set.Lookup(name) |
||||||
|
if f != nil { |
||||||
|
parsed, err := strconv.ParseUint(f.Value.String(), 0, 64) |
||||||
|
if err != nil { |
||||||
|
return 0 |
||||||
|
} |
||||||
|
return parsed |
||||||
|
} |
||||||
|
return 0 |
||||||
|
} |
||||||
|
|
||||||
|
// UintFlag is a flag with type uint
|
||||||
|
type UintFlag struct { |
||||||
|
Name string |
||||||
|
Usage string |
||||||
|
EnvVar string |
||||||
|
Hidden bool |
||||||
|
Value uint |
||||||
|
Destination *uint |
||||||
|
} |
||||||
|
|
||||||
|
// String returns a readable representation of this value
|
||||||
|
// (for usage defaults)
|
||||||
|
func (f UintFlag) String() string { |
||||||
|
return FlagStringer(f) |
||||||
|
} |
||||||
|
|
||||||
|
// GetName returns the name of the flag
|
||||||
|
func (f UintFlag) GetName() string { |
||||||
|
return f.Name |
||||||
|
} |
||||||
|
|
||||||
|
// Uint looks up the value of a local UintFlag, returns
|
||||||
|
// 0 if not found
|
||||||
|
func (c *Context) Uint(name string) uint { |
||||||
|
return lookupUint(name, c.flagSet) |
||||||
|
} |
||||||
|
|
||||||
|
// GlobalUint looks up the value of a global UintFlag, returns
|
||||||
|
// 0 if not found
|
||||||
|
func (c *Context) GlobalUint(name string) uint { |
||||||
|
if fs := lookupGlobalFlagSet(name, c); fs != nil { |
||||||
|
return lookupUint(name, fs) |
||||||
|
} |
||||||
|
return 0 |
||||||
|
} |
||||||
|
|
||||||
|
func lookupUint(name string, set *flag.FlagSet) uint { |
||||||
|
f := set.Lookup(name) |
||||||
|
if f != nil { |
||||||
|
parsed, err := strconv.ParseUint(f.Value.String(), 0, 64) |
||||||
|
if err != nil { |
||||||
|
return 0 |
||||||
|
} |
||||||
|
return uint(parsed) |
||||||
|
} |
||||||
|
return 0 |
||||||
|
} |
@ -0,0 +1,28 @@ |
|||||||
|
package cli |
||||||
|
|
||||||
|
// BashCompleteFunc is an action to execute when the bash-completion flag is set
|
||||||
|
type BashCompleteFunc func(*Context) |
||||||
|
|
||||||
|
// BeforeFunc is an action to execute before any subcommands are run, but after
|
||||||
|
// the context is ready if a non-nil error is returned, no subcommands are run
|
||||||
|
type BeforeFunc func(*Context) error |
||||||
|
|
||||||
|
// AfterFunc is an action to execute after any subcommands are run, but after the
|
||||||
|
// subcommand has finished it is run even if Action() panics
|
||||||
|
type AfterFunc func(*Context) error |
||||||
|
|
||||||
|
// ActionFunc is the action to execute when no subcommands are specified
|
||||||
|
type ActionFunc func(*Context) error |
||||||
|
|
||||||
|
// CommandNotFoundFunc is executed if the proper command cannot be found
|
||||||
|
type CommandNotFoundFunc func(*Context, string) |
||||||
|
|
||||||
|
// OnUsageErrorFunc is executed if an usage error occurs. This is useful for displaying
|
||||||
|
// customized usage error messages. This function is able to replace the
|
||||||
|
// original error messages. If this function is not set, the "Incorrect usage"
|
||||||
|
// is displayed and the execution is interrupted.
|
||||||
|
type OnUsageErrorFunc func(context *Context, err error, isSubcommand bool) error |
||||||
|
|
||||||
|
// FlagStringFunc is used by the help generation to display a flag, which is
|
||||||
|
// expected to be a single line.
|
||||||
|
type FlagStringFunc func(Flag) string |
@ -0,0 +1,255 @@ |
|||||||
|
#!/usr/bin/env python |
||||||
|
""" |
||||||
|
The flag types that ship with the cli library have many things in common, and |
||||||
|
so we can take advantage of the `go generate` command to create much of the |
||||||
|
source code from a list of definitions. These definitions attempt to cover |
||||||
|
the parts that vary between flag types, and should evolve as needed. |
||||||
|
|
||||||
|
An example of the minimum definition needed is: |
||||||
|
|
||||||
|
{ |
||||||
|
"name": "SomeType", |
||||||
|
"type": "sometype", |
||||||
|
"context_default": "nil" |
||||||
|
} |
||||||
|
|
||||||
|
In this example, the code generated for the `cli` package will include a type |
||||||
|
named `SomeTypeFlag` that is expected to wrap a value of type `sometype`. |
||||||
|
Fetching values by name via `*cli.Context` will default to a value of `nil`. |
||||||
|
|
||||||
|
A more complete, albeit somewhat redundant, example showing all available |
||||||
|
definition keys is: |
||||||
|
|
||||||
|
{ |
||||||
|
"name": "VeryMuchType", |
||||||
|
"type": "*VeryMuchType", |
||||||
|
"value": true, |
||||||
|
"dest": false, |
||||||
|
"doctail": " which really only wraps a []float64, oh well!", |
||||||
|
"context_type": "[]float64", |
||||||
|
"context_default": "nil", |
||||||
|
"parser": "parseVeryMuchType(f.Value.String())", |
||||||
|
"parser_cast": "[]float64(parsed)" |
||||||
|
} |
||||||
|
|
||||||
|
The meaning of each field is as follows: |
||||||
|
|
||||||
|
name (string) - The type "name", which will be suffixed with |
||||||
|
`Flag` when generating the type definition |
||||||
|
for `cli` and the wrapper type for `altsrc` |
||||||
|
type (string) - The type that the generated `Flag` type for `cli` |
||||||
|
is expected to "contain" as its `.Value` member |
||||||
|
value (bool) - Should the generated `cli` type have a `Value` |
||||||
|
member? |
||||||
|
dest (bool) - Should the generated `cli` type support a |
||||||
|
destination pointer? |
||||||
|
doctail (string) - Additional docs for the `cli` flag type comment |
||||||
|
context_type (string) - The literal type used in the `*cli.Context` |
||||||
|
reader func signature |
||||||
|
context_default (string) - The literal value used as the default by the |
||||||
|
`*cli.Context` reader funcs when no value is |
||||||
|
present |
||||||
|
parser (string) - Literal code used to parse the flag `f`, |
||||||
|
expected to have a return signature of |
||||||
|
(value, error) |
||||||
|
parser_cast (string) - Literal code used to cast the `parsed` value |
||||||
|
returned from the `parser` code |
||||||
|
""" |
||||||
|
|
||||||
|
from __future__ import print_function, unicode_literals |
||||||
|
|
||||||
|
import argparse |
||||||
|
import json |
||||||
|
import os |
||||||
|
import subprocess |
||||||
|
import sys |
||||||
|
import tempfile |
||||||
|
import textwrap |
||||||
|
|
||||||
|
|
||||||
|
class _FancyFormatter(argparse.ArgumentDefaultsHelpFormatter, |
||||||
|
argparse.RawDescriptionHelpFormatter): |
||||||
|
pass |
||||||
|
|
||||||
|
|
||||||
|
def main(sysargs=sys.argv[:]): |
||||||
|
parser = argparse.ArgumentParser( |
||||||
|
description='Generate flag type code!', |
||||||
|
formatter_class=_FancyFormatter) |
||||||
|
parser.add_argument( |
||||||
|
'package', |
||||||
|
type=str, default='cli', choices=_WRITEFUNCS.keys(), |
||||||
|
help='Package for which flag types will be generated' |
||||||
|
) |
||||||
|
parser.add_argument( |
||||||
|
'-i', '--in-json', |
||||||
|
type=argparse.FileType('r'), |
||||||
|
default=sys.stdin, |
||||||
|
help='Input JSON file which defines each type to be generated' |
||||||
|
) |
||||||
|
parser.add_argument( |
||||||
|
'-o', '--out-go', |
||||||
|
type=argparse.FileType('w'), |
||||||
|
default=sys.stdout, |
||||||
|
help='Output file/stream to which generated source will be written' |
||||||
|
) |
||||||
|
parser.epilog = __doc__ |
||||||
|
|
||||||
|
args = parser.parse_args(sysargs[1:]) |
||||||
|
_generate_flag_types(_WRITEFUNCS[args.package], args.out_go, args.in_json) |
||||||
|
return 0 |
||||||
|
|
||||||
|
|
||||||
|
def _generate_flag_types(writefunc, output_go, input_json): |
||||||
|
types = json.load(input_json) |
||||||
|
|
||||||
|
tmp = tempfile.NamedTemporaryFile(suffix='.go', delete=False) |
||||||
|
writefunc(tmp, types) |
||||||
|
tmp.close() |
||||||
|
|
||||||
|
new_content = subprocess.check_output( |
||||||
|
['goimports', tmp.name] |
||||||
|
).decode('utf-8') |
||||||
|
|
||||||
|
print(new_content, file=output_go, end='') |
||||||
|
output_go.flush() |
||||||
|
os.remove(tmp.name) |
||||||
|
|
||||||
|
|
||||||
|
def _set_typedef_defaults(typedef): |
||||||
|
typedef.setdefault('doctail', '') |
||||||
|
typedef.setdefault('context_type', typedef['type']) |
||||||
|
typedef.setdefault('dest', True) |
||||||
|
typedef.setdefault('value', True) |
||||||
|
typedef.setdefault('parser', 'f.Value, error(nil)') |
||||||
|
typedef.setdefault('parser_cast', 'parsed') |
||||||
|
|
||||||
|
|
||||||
|
def _write_cli_flag_types(outfile, types): |
||||||
|
_fwrite(outfile, """\ |
||||||
|
package cli |
||||||
|
|
||||||
|
// WARNING: This file is generated! |
||||||
|
|
||||||
|
""") |
||||||
|
|
||||||
|
for typedef in types: |
||||||
|
_set_typedef_defaults(typedef) |
||||||
|
|
||||||
|
_fwrite(outfile, """\ |
||||||
|
// {name}Flag is a flag with type {type}{doctail} |
||||||
|
type {name}Flag struct {{ |
||||||
|
Name string |
||||||
|
Usage string |
||||||
|
EnvVar string |
||||||
|
Hidden bool |
||||||
|
""".format(**typedef)) |
||||||
|
|
||||||
|
if typedef['value']: |
||||||
|
_fwrite(outfile, """\ |
||||||
|
Value {type} |
||||||
|
""".format(**typedef)) |
||||||
|
|
||||||
|
if typedef['dest']: |
||||||
|
_fwrite(outfile, """\ |
||||||
|
Destination *{type} |
||||||
|
""".format(**typedef)) |
||||||
|
|
||||||
|
_fwrite(outfile, "\n}\n\n") |
||||||
|
|
||||||
|
_fwrite(outfile, """\ |
||||||
|
// String returns a readable representation of this value |
||||||
|
// (for usage defaults) |
||||||
|
func (f {name}Flag) String() string {{ |
||||||
|
return FlagStringer(f) |
||||||
|
}} |
||||||
|
|
||||||
|
// GetName returns the name of the flag |
||||||
|
func (f {name}Flag) GetName() string {{ |
||||||
|
return f.Name |
||||||
|
}} |
||||||
|
|
||||||
|
// {name} looks up the value of a local {name}Flag, returns |
||||||
|
// {context_default} if not found |
||||||
|
func (c *Context) {name}(name string) {context_type} {{ |
||||||
|
return lookup{name}(name, c.flagSet) |
||||||
|
}} |
||||||
|
|
||||||
|
// Global{name} looks up the value of a global {name}Flag, returns |
||||||
|
// {context_default} if not found |
||||||
|
func (c *Context) Global{name}(name string) {context_type} {{ |
||||||
|
if fs := lookupGlobalFlagSet(name, c); fs != nil {{ |
||||||
|
return lookup{name}(name, fs) |
||||||
|
}} |
||||||
|
return {context_default} |
||||||
|
}} |
||||||
|
|
||||||
|
func lookup{name}(name string, set *flag.FlagSet) {context_type} {{ |
||||||
|
f := set.Lookup(name) |
||||||
|
if f != nil {{ |
||||||
|
parsed, err := {parser} |
||||||
|
if err != nil {{ |
||||||
|
return {context_default} |
||||||
|
}} |
||||||
|
return {parser_cast} |
||||||
|
}} |
||||||
|
return {context_default} |
||||||
|
}} |
||||||
|
""".format(**typedef)) |
||||||
|
|
||||||
|
|
||||||
|
def _write_altsrc_flag_types(outfile, types): |
||||||
|
_fwrite(outfile, """\ |
||||||
|
package altsrc |
||||||
|
|
||||||
|
import ( |
||||||
|
"gopkg.in/urfave/cli.v1" |
||||||
|
) |
||||||
|
|
||||||
|
// WARNING: This file is generated! |
||||||
|
|
||||||
|
""") |
||||||
|
|
||||||
|
for typedef in types: |
||||||
|
_set_typedef_defaults(typedef) |
||||||
|
|
||||||
|
_fwrite(outfile, """\ |
||||||
|
// {name}Flag is the flag type that wraps cli.{name}Flag to allow |
||||||
|
// for other values to be specified |
||||||
|
type {name}Flag struct {{ |
||||||
|
cli.{name}Flag |
||||||
|
set *flag.FlagSet |
||||||
|
}} |
||||||
|
|
||||||
|
// New{name}Flag creates a new {name}Flag |
||||||
|
func New{name}Flag(fl cli.{name}Flag) *{name}Flag {{ |
||||||
|
return &{name}Flag{{{name}Flag: fl, set: nil}} |
||||||
|
}} |
||||||
|
|
||||||
|
// Apply saves the flagSet for later usage calls, then calls the |
||||||
|
// wrapped {name}Flag.Apply |
||||||
|
func (f *{name}Flag) Apply(set *flag.FlagSet) {{ |
||||||
|
f.set = set |
||||||
|
f.{name}Flag.Apply(set) |
||||||
|
}} |
||||||
|
|
||||||
|
// ApplyWithError saves the flagSet for later usage calls, then calls the |
||||||
|
// wrapped {name}Flag.ApplyWithError |
||||||
|
func (f *{name}Flag) ApplyWithError(set *flag.FlagSet) error {{ |
||||||
|
f.set = set |
||||||
|
return f.{name}Flag.ApplyWithError(set) |
||||||
|
}} |
||||||
|
""".format(**typedef)) |
||||||
|
|
||||||
|
|
||||||
|
def _fwrite(outfile, text): |
||||||
|
print(textwrap.dedent(text), end='', file=outfile) |
||||||
|
|
||||||
|
|
||||||
|
_WRITEFUNCS = { |
||||||
|
'cli': _write_cli_flag_types, |
||||||
|
'altsrc': _write_altsrc_flag_types |
||||||
|
} |
||||||
|
|
||||||
|
if __name__ == '__main__': |
||||||
|
sys.exit(main()) |
@ -0,0 +1,338 @@ |
|||||||
|
package cli |
||||||
|
|
||||||
|
import ( |
||||||
|
"fmt" |
||||||
|
"io" |
||||||
|
"os" |
||||||
|
"strings" |
||||||
|
"text/tabwriter" |
||||||
|
"text/template" |
||||||
|
) |
||||||
|
|
||||||
|
// AppHelpTemplate is the text template for the Default help topic.
|
||||||
|
// cli.go uses text/template to render templates. You can
|
||||||
|
// render custom help text by setting this variable.
|
||||||
|
var AppHelpTemplate = `NAME: |
||||||
|
{{.Name}}{{if .Usage}} - {{.Usage}}{{end}} |
||||||
|
|
||||||
|
USAGE: |
||||||
|
{{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}} {{if .VisibleFlags}}[global options]{{end}}{{if .Commands}} command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Version}}{{if not .HideVersion}} |
||||||
|
|
||||||
|
VERSION: |
||||||
|
{{.Version}}{{end}}{{end}}{{if .Description}} |
||||||
|
|
||||||
|
DESCRIPTION: |
||||||
|
{{.Description}}{{end}}{{if len .Authors}} |
||||||
|
|
||||||
|
AUTHOR{{with $length := len .Authors}}{{if ne 1 $length}}S{{end}}{{end}}: |
||||||
|
{{range $index, $author := .Authors}}{{if $index}} |
||||||
|
{{end}}{{$author}}{{end}}{{end}}{{if .VisibleCommands}} |
||||||
|
|
||||||
|
COMMANDS:{{range .VisibleCategories}}{{if .Name}} |
||||||
|
{{.Name}}:{{end}}{{range .VisibleCommands}} |
||||||
|
{{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{end}}{{end}}{{if .VisibleFlags}} |
||||||
|
|
||||||
|
GLOBAL OPTIONS: |
||||||
|
{{range $index, $option := .VisibleFlags}}{{if $index}} |
||||||
|
{{end}}{{$option}}{{end}}{{end}}{{if .Copyright}} |
||||||
|
|
||||||
|
COPYRIGHT: |
||||||
|
{{.Copyright}}{{end}} |
||||||
|
` |
||||||
|
|
||||||
|
// CommandHelpTemplate is the text template for the command help topic.
|
||||||
|
// cli.go uses text/template to render templates. You can
|
||||||
|
// render custom help text by setting this variable.
|
||||||
|
var CommandHelpTemplate = `NAME: |
||||||
|
{{.HelpName}} - {{.Usage}} |
||||||
|
|
||||||
|
USAGE: |
||||||
|
{{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}}{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Category}} |
||||||
|
|
||||||
|
CATEGORY: |
||||||
|
{{.Category}}{{end}}{{if .Description}} |
||||||
|
|
||||||
|
DESCRIPTION: |
||||||
|
{{.Description}}{{end}}{{if .VisibleFlags}} |
||||||
|
|
||||||
|
OPTIONS: |
||||||
|
{{range .VisibleFlags}}{{.}} |
||||||
|
{{end}}{{end}} |
||||||
|
` |
||||||
|
|
||||||
|
// SubcommandHelpTemplate is the text template for the subcommand help topic.
|
||||||
|
// cli.go uses text/template to render templates. You can
|
||||||
|
// render custom help text by setting this variable.
|
||||||
|
var SubcommandHelpTemplate = `NAME: |
||||||
|
{{.HelpName}} - {{if .Description}}{{.Description}}{{else}}{{.Usage}}{{end}} |
||||||
|
|
||||||
|
USAGE: |
||||||
|
{{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}} command{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}} |
||||||
|
|
||||||
|
COMMANDS:{{range .VisibleCategories}}{{if .Name}} |
||||||
|
{{.Name}}:{{end}}{{range .VisibleCommands}} |
||||||
|
{{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}} |
||||||
|
{{end}}{{if .VisibleFlags}} |
||||||
|
OPTIONS: |
||||||
|
{{range .VisibleFlags}}{{.}} |
||||||
|
{{end}}{{end}} |
||||||
|
` |
||||||
|
|
||||||
|
var helpCommand = Command{ |
||||||
|
Name: "help", |
||||||
|
Aliases: []string{"h"}, |
||||||
|
Usage: "Shows a list of commands or help for one command", |
||||||
|
ArgsUsage: "[command]", |
||||||
|
Action: func(c *Context) error { |
||||||
|
args := c.Args() |
||||||
|
if args.Present() { |
||||||
|
return ShowCommandHelp(c, args.First()) |
||||||
|
} |
||||||
|
|
||||||
|
ShowAppHelp(c) |
||||||
|
return nil |
||||||
|
}, |
||||||
|
} |
||||||
|
|
||||||
|
var helpSubcommand = Command{ |
||||||
|
Name: "help", |
||||||
|
Aliases: []string{"h"}, |
||||||
|
Usage: "Shows a list of commands or help for one command", |
||||||
|
ArgsUsage: "[command]", |
||||||
|
Action: func(c *Context) error { |
||||||
|
args := c.Args() |
||||||
|
if args.Present() { |
||||||
|
return ShowCommandHelp(c, args.First()) |
||||||
|
} |
||||||
|
|
||||||
|
return ShowSubcommandHelp(c) |
||||||
|
}, |
||||||
|
} |
||||||
|
|
||||||
|
// Prints help for the App or Command
|
||||||
|
type helpPrinter func(w io.Writer, templ string, data interface{}) |
||||||
|
|
||||||
|
// Prints help for the App or Command with custom template function.
|
||||||
|
type helpPrinterCustom func(w io.Writer, templ string, data interface{}, customFunc map[string]interface{}) |
||||||
|
|
||||||
|
// HelpPrinter is a function that writes the help output. If not set a default
|
||||||
|
// is used. The function signature is:
|
||||||
|
// func(w io.Writer, templ string, data interface{})
|
||||||
|
var HelpPrinter helpPrinter = printHelp |
||||||
|
|
||||||
|
// HelpPrinterCustom is same as HelpPrinter but
|
||||||
|
// takes a custom function for template function map.
|
||||||
|
var HelpPrinterCustom helpPrinterCustom = printHelpCustom |
||||||
|
|
||||||
|
// VersionPrinter prints the version for the App
|
||||||
|
var VersionPrinter = printVersion |
||||||
|
|
||||||
|
// ShowAppHelpAndExit - Prints the list of subcommands for the app and exits with exit code.
|
||||||
|
func ShowAppHelpAndExit(c *Context, exitCode int) { |
||||||
|
ShowAppHelp(c) |
||||||
|
os.Exit(exitCode) |
||||||
|
} |
||||||
|
|
||||||
|
// ShowAppHelp is an action that displays the help.
|
||||||
|
func ShowAppHelp(c *Context) (err error) { |
||||||
|
if c.App.CustomAppHelpTemplate == "" { |
||||||
|
HelpPrinter(c.App.Writer, AppHelpTemplate, c.App) |
||||||
|
return |
||||||
|
} |
||||||
|
customAppData := func() map[string]interface{} { |
||||||
|
if c.App.ExtraInfo == nil { |
||||||
|
return nil |
||||||
|
} |
||||||
|
return map[string]interface{}{ |
||||||
|
"ExtraInfo": c.App.ExtraInfo, |
||||||
|
} |
||||||
|
} |
||||||
|
HelpPrinterCustom(c.App.Writer, c.App.CustomAppHelpTemplate, c.App, customAppData()) |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// DefaultAppComplete prints the list of subcommands as the default app completion method
|
||||||
|
func DefaultAppComplete(c *Context) { |
||||||
|
for _, command := range c.App.Commands { |
||||||
|
if command.Hidden { |
||||||
|
continue |
||||||
|
} |
||||||
|
for _, name := range command.Names() { |
||||||
|
fmt.Fprintln(c.App.Writer, name) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// ShowCommandHelpAndExit - exits with code after showing help
|
||||||
|
func ShowCommandHelpAndExit(c *Context, command string, code int) { |
||||||
|
ShowCommandHelp(c, command) |
||||||
|
os.Exit(code) |
||||||
|
} |
||||||
|
|
||||||
|
// ShowCommandHelp prints help for the given command
|
||||||
|
func ShowCommandHelp(ctx *Context, command string) error { |
||||||
|
// show the subcommand help for a command with subcommands
|
||||||
|
if command == "" { |
||||||
|
HelpPrinter(ctx.App.Writer, SubcommandHelpTemplate, ctx.App) |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
for _, c := range ctx.App.Commands { |
||||||
|
if c.HasName(command) { |
||||||
|
if c.CustomHelpTemplate != "" { |
||||||
|
HelpPrinterCustom(ctx.App.Writer, c.CustomHelpTemplate, c, nil) |
||||||
|
} else { |
||||||
|
HelpPrinter(ctx.App.Writer, CommandHelpTemplate, c) |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if ctx.App.CommandNotFound == nil { |
||||||
|
return NewExitError(fmt.Sprintf("No help topic for '%v'", command), 3) |
||||||
|
} |
||||||
|
|
||||||
|
ctx.App.CommandNotFound(ctx, command) |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// ShowSubcommandHelp prints help for the given subcommand
|
||||||
|
func ShowSubcommandHelp(c *Context) error { |
||||||
|
return ShowCommandHelp(c, c.Command.Name) |
||||||
|
} |
||||||
|
|
||||||
|
// ShowVersion prints the version number of the App
|
||||||
|
func ShowVersion(c *Context) { |
||||||
|
VersionPrinter(c) |
||||||
|
} |
||||||
|
|
||||||
|
func printVersion(c *Context) { |
||||||
|
fmt.Fprintf(c.App.Writer, "%v version %v\n", c.App.Name, c.App.Version) |
||||||
|
} |
||||||
|
|
||||||
|
// ShowCompletions prints the lists of commands within a given context
|
||||||
|
func ShowCompletions(c *Context) { |
||||||
|
a := c.App |
||||||
|
if a != nil && a.BashComplete != nil { |
||||||
|
a.BashComplete(c) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// ShowCommandCompletions prints the custom completions for a given command
|
||||||
|
func ShowCommandCompletions(ctx *Context, command string) { |
||||||
|
c := ctx.App.Command(command) |
||||||
|
if c != nil && c.BashComplete != nil { |
||||||
|
c.BashComplete(ctx) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func printHelpCustom(out io.Writer, templ string, data interface{}, customFunc map[string]interface{}) { |
||||||
|
funcMap := template.FuncMap{ |
||||||
|
"join": strings.Join, |
||||||
|
} |
||||||
|
if customFunc != nil { |
||||||
|
for key, value := range customFunc { |
||||||
|
funcMap[key] = value |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
w := tabwriter.NewWriter(out, 1, 8, 2, ' ', 0) |
||||||
|
t := template.Must(template.New("help").Funcs(funcMap).Parse(templ)) |
||||||
|
err := t.Execute(w, data) |
||||||
|
if err != nil { |
||||||
|
// If the writer is closed, t.Execute will fail, and there's nothing
|
||||||
|
// we can do to recover.
|
||||||
|
if os.Getenv("CLI_TEMPLATE_ERROR_DEBUG") != "" { |
||||||
|
fmt.Fprintf(ErrWriter, "CLI TEMPLATE ERROR: %#v\n", err) |
||||||
|
} |
||||||
|
return |
||||||
|
} |
||||||
|
w.Flush() |
||||||
|
} |
||||||
|
|
||||||
|
func printHelp(out io.Writer, templ string, data interface{}) { |
||||||
|
printHelpCustom(out, templ, data, nil) |
||||||
|
} |
||||||
|
|
||||||
|
func checkVersion(c *Context) bool { |
||||||
|
found := false |
||||||
|
if VersionFlag.GetName() != "" { |
||||||
|
eachName(VersionFlag.GetName(), func(name string) { |
||||||
|
if c.GlobalBool(name) || c.Bool(name) { |
||||||
|
found = true |
||||||
|
} |
||||||
|
}) |
||||||
|
} |
||||||
|
return found |
||||||
|
} |
||||||
|
|
||||||
|
func checkHelp(c *Context) bool { |
||||||
|
found := false |
||||||
|
if HelpFlag.GetName() != "" { |
||||||
|
eachName(HelpFlag.GetName(), func(name string) { |
||||||
|
if c.GlobalBool(name) || c.Bool(name) { |
||||||
|
found = true |
||||||
|
} |
||||||
|
}) |
||||||
|
} |
||||||
|
return found |
||||||
|
} |
||||||
|
|
||||||
|
func checkCommandHelp(c *Context, name string) bool { |
||||||
|
if c.Bool("h") || c.Bool("help") { |
||||||
|
ShowCommandHelp(c, name) |
||||||
|
return true |
||||||
|
} |
||||||
|
|
||||||
|
return false |
||||||
|
} |
||||||
|
|
||||||
|
func checkSubcommandHelp(c *Context) bool { |
||||||
|
if c.Bool("h") || c.Bool("help") { |
||||||
|
ShowSubcommandHelp(c) |
||||||
|
return true |
||||||
|
} |
||||||
|
|
||||||
|
return false |
||||||
|
} |
||||||
|
|
||||||
|
func checkShellCompleteFlag(a *App, arguments []string) (bool, []string) { |
||||||
|
if !a.EnableBashCompletion { |
||||||
|
return false, arguments |
||||||
|
} |
||||||
|
|
||||||
|
pos := len(arguments) - 1 |
||||||
|
lastArg := arguments[pos] |
||||||
|
|
||||||
|
if lastArg != "--"+BashCompletionFlag.GetName() { |
||||||
|
return false, arguments |
||||||
|
} |
||||||
|
|
||||||
|
return true, arguments[:pos] |
||||||
|
} |
||||||
|
|
||||||
|
func checkCompletions(c *Context) bool { |
||||||
|
if !c.shellComplete { |
||||||
|
return false |
||||||
|
} |
||||||
|
|
||||||
|
if args := c.Args(); args.Present() { |
||||||
|
name := args.First() |
||||||
|
if cmd := c.App.Command(name); cmd != nil { |
||||||
|
// let the command handle the completion
|
||||||
|
return false |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
ShowCompletions(c) |
||||||
|
return true |
||||||
|
} |
||||||
|
|
||||||
|
func checkCommandCompletions(c *Context, name string) bool { |
||||||
|
if !c.shellComplete { |
||||||
|
return false |
||||||
|
} |
||||||
|
|
||||||
|
ShowCommandCompletions(c, name) |
||||||
|
return true |
||||||
|
} |
@ -0,0 +1,122 @@ |
|||||||
|
#!/usr/bin/env python |
||||||
|
from __future__ import print_function |
||||||
|
|
||||||
|
import argparse |
||||||
|
import os |
||||||
|
import sys |
||||||
|
import tempfile |
||||||
|
|
||||||
|
from subprocess import check_call, check_output |
||||||
|
|
||||||
|
|
||||||
|
PACKAGE_NAME = os.environ.get( |
||||||
|
'CLI_PACKAGE_NAME', 'github.com/urfave/cli' |
||||||
|
) |
||||||
|
|
||||||
|
|
||||||
|
def main(sysargs=sys.argv[:]): |
||||||
|
targets = { |
||||||
|
'vet': _vet, |
||||||
|
'test': _test, |
||||||
|
'gfmrun': _gfmrun, |
||||||
|
'toc': _toc, |
||||||
|
'gen': _gen, |
||||||
|
} |
||||||
|
|
||||||
|
parser = argparse.ArgumentParser() |
||||||
|
parser.add_argument( |
||||||
|
'target', nargs='?', choices=tuple(targets.keys()), default='test' |
||||||
|
) |
||||||
|
args = parser.parse_args(sysargs[1:]) |
||||||
|
|
||||||
|
targets[args.target]() |
||||||
|
return 0 |
||||||
|
|
||||||
|
|
||||||
|
def _test(): |
||||||
|
if check_output('go version'.split()).split()[2] < 'go1.2': |
||||||
|
_run('go test -v .') |
||||||
|
return |
||||||
|
|
||||||
|
coverprofiles = [] |
||||||
|
for subpackage in ['', 'altsrc']: |
||||||
|
coverprofile = 'cli.coverprofile' |
||||||
|
if subpackage != '': |
||||||
|
coverprofile = '{}.coverprofile'.format(subpackage) |
||||||
|
|
||||||
|
coverprofiles.append(coverprofile) |
||||||
|
|
||||||
|
_run('go test -v'.split() + [ |
||||||
|
'-coverprofile={}'.format(coverprofile), |
||||||
|
('{}/{}'.format(PACKAGE_NAME, subpackage)).rstrip('/') |
||||||
|
]) |
||||||
|
|
||||||
|
combined_name = _combine_coverprofiles(coverprofiles) |
||||||
|
_run('go tool cover -func={}'.format(combined_name)) |
||||||
|
os.remove(combined_name) |
||||||
|
|
||||||
|
|
||||||
|
def _gfmrun(): |
||||||
|
go_version = check_output('go version'.split()).split()[2] |
||||||
|
if go_version < 'go1.3': |
||||||
|
print('runtests: skip on {}'.format(go_version), file=sys.stderr) |
||||||
|
return |
||||||
|
_run(['gfmrun', '-c', str(_gfmrun_count()), '-s', 'README.md']) |
||||||
|
|
||||||
|
|
||||||
|
def _vet(): |
||||||
|
_run('go vet ./...') |
||||||
|
|
||||||
|
|
||||||
|
def _toc(): |
||||||
|
_run('node_modules/.bin/markdown-toc -i README.md') |
||||||
|
_run('git diff --exit-code') |
||||||
|
|
||||||
|
|
||||||
|
def _gen(): |
||||||
|
go_version = check_output('go version'.split()).split()[2] |
||||||
|
if go_version < 'go1.5': |
||||||
|
print('runtests: skip on {}'.format(go_version), file=sys.stderr) |
||||||
|
return |
||||||
|
|
||||||
|
_run('go generate ./...') |
||||||
|
_run('git diff --exit-code') |
||||||
|
|
||||||
|
|
||||||
|
def _run(command): |
||||||
|
if hasattr(command, 'split'): |
||||||
|
command = command.split() |
||||||
|
print('runtests: {}'.format(' '.join(command)), file=sys.stderr) |
||||||
|
check_call(command) |
||||||
|
|
||||||
|
|
||||||
|
def _gfmrun_count(): |
||||||
|
with open('README.md') as infile: |
||||||
|
lines = infile.read().splitlines() |
||||||
|
return len(filter(_is_go_runnable, lines)) |
||||||
|
|
||||||
|
|
||||||
|
def _is_go_runnable(line): |
||||||
|
return line.startswith('package main') |
||||||
|
|
||||||
|
|
||||||
|
def _combine_coverprofiles(coverprofiles): |
||||||
|
combined = tempfile.NamedTemporaryFile( |
||||||
|
suffix='.coverprofile', delete=False |
||||||
|
) |
||||||
|
combined.write('mode: set\n') |
||||||
|
|
||||||
|
for coverprofile in coverprofiles: |
||||||
|
with open(coverprofile, 'r') as infile: |
||||||
|
for line in infile.readlines(): |
||||||
|
if not line.startswith('mode: '): |
||||||
|
combined.write(line) |
||||||
|
|
||||||
|
combined.flush() |
||||||
|
name = combined.name |
||||||
|
combined.close() |
||||||
|
return name |
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__': |
||||||
|
sys.exit(main()) |
@ -0,0 +1,4 @@ |
|||||||
|
# github.com/mitchellh/go-homedir v1.1.0 |
||||||
|
github.com/mitchellh/go-homedir |
||||||
|
# github.com/urfave/cli v1.20.0 |
||||||
|
github.com/urfave/cli |
Loading…
Reference in new issue