Merge pull request #184 from vbatts/go_updates

go: updating modules
This commit is contained in:
Vincent Batts 2023-03-22 10:49:02 -04:00 committed by GitHub
commit 249dc61329
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
40 changed files with 2475 additions and 731 deletions

2
go.mod
View File

@ -6,7 +6,7 @@ require (
github.com/davecgh/go-spew v1.1.1 github.com/davecgh/go-spew v1.1.1
github.com/fatih/color v1.13.0 github.com/fatih/color v1.13.0
github.com/sirupsen/logrus v1.8.1 github.com/sirupsen/logrus v1.8.1
github.com/urfave/cli/v2 v2.10.3 github.com/urfave/cli/v2 v2.24.4
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e
) )

7
go.sum
View File

@ -1,4 +1,4 @@
github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
@ -19,8 +19,8 @@ github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/urfave/cli/v2 v2.10.3 h1:oi571Fxz5aHugfBAJd5nkwSk3fzATXtMlpxdLylSCMo= github.com/urfave/cli/v2 v2.24.4 h1:0gyJJEBYtCV87zI/x2nZCPyDxD51K6xM8SkwjHFCNEU=
github.com/urfave/cli/v2 v2.10.3/go.mod h1:f8iq5LtQ/bLxafbdBSLPPNsgaW0l/2fYYEHhAyPlwvo= github.com/urfave/cli/v2 v2.24.4/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d h1:sK3txAijHtOK88l68nt020reeT1ZdKLIYetKl95FzVY= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d h1:sK3txAijHtOK88l68nt020reeT1ZdKLIYetKl95FzVY=
@ -38,7 +38,6 @@ golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e h1:CsOuNlbOuf0mzxJIefr6Q4uAU
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@ -1,10 +1,13 @@
*.coverprofile *.coverprofile
*.exe
*.orig *.orig
vendor .*envrc
.envrc
.idea .idea
internal/*/built-example
coverage.txt
/.local/ /.local/
/site/ /site/
coverage.txt
internal/*/built-example
vendor
/cmd/urfave-cli-genflags/urfave-cli-genflags
*.exe *.exe

View File

@ -4,8 +4,10 @@
# are very important so that maintainers and contributors can focus their # are very important so that maintainers and contributors can focus their
# attention on files that are primarily Go. # attention on files that are primarily Go.
GO_RUN_BUILD := go run internal/build/build.go
.PHONY: all .PHONY: all
all: generate vet tag-test test check-binary-size tag-check-binary-size gfmrun v2diff all: generate vet test check-binary-size gfmrun yamlfmt v2diff
# NOTE: this is a special catch-all rule to run any of the commands # NOTE: this is a special catch-all rule to run any of the commands
# defined in internal/build/build.go with optional arguments passed # defined in internal/build/build.go with optional arguments passed
@ -13,28 +15,12 @@ all: generate vet tag-test test check-binary-size tag-check-binary-size gfmrun v
# #
# $ make test GFLAGS='--packages cli' # $ make test GFLAGS='--packages cli'
%: %:
go run internal/build/build.go $(GFLAGS) $* $(FLAGS) $(GO_RUN_BUILD) $(GFLAGS) $* $(FLAGS)
.PHONY: tag-test
tag-test:
go run internal/build/build.go -tags urfave_cli_no_docs test
.PHONY: tag-check-binary-size
tag-check-binary-size:
go run internal/build/build.go -tags urfave_cli_no_docs check-binary-size
.PHONY: gfmrun
gfmrun:
go run internal/build/build.go gfmrun docs/v2/manual.md
.PHONY: docs .PHONY: docs
docs: docs:
mkdocs build mkdocs build
.PHONY: docs-deps
docs-deps:
pip install -r mkdocs-requirements.txt
.PHONY: serve-docs .PHONY: serve-docs
serve-docs: serve-docs:
mkdocs serve mkdocs serve

View File

@ -1,9 +1,9 @@
# cli # cli
[![GoDoc](https://godoc.org/github.com/urfave/cli?status.svg)](https://pkg.go.dev/github.com/urfave/cli/v2) [![Run Tests](https://github.com/urfave/cli/actions/workflows/cli.yml/badge.svg?branch=v2-maint)](https://github.com/urfave/cli/actions/workflows/cli.yml)
[![codebeat](https://codebeat.co/badges/0a8f30aa-f975-404b-b878-5fab3ae1cc5f)](https://codebeat.co/projects/github-com-urfave-cli) [![Go Reference](https://pkg.go.dev/badge/github.com/urfave/cli/v2.svg)](https://pkg.go.dev/github.com/urfave/cli/v2)
[![Go Report Card](https://goreportcard.com/badge/urfave/cli)](https://goreportcard.com/report/urfave/cli) [![Go Report Card](https://goreportcard.com/badge/github.com/urfave/cli/v2)](https://goreportcard.com/report/github.com/urfave/cli/v2)
[![codecov](https://codecov.io/gh/urfave/cli/branch/main/graph/badge.svg)](https://codecov.io/gh/urfave/cli) [![codecov](https://codecov.io/gh/urfave/cli/branch/v2-maint/graph/badge.svg?token=t9YGWLh05g)](https://app.codecov.io/gh/urfave/cli/tree/v2-maint)
cli is a simple, fast, and fun package for building command line apps in Go. The cli is a simple, fast, and fun package for building command line apps in Go. The
goal is to enable developers to write fast and distributable command line goal is to enable developers to write fast and distributable command line

View File

@ -8,6 +8,7 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"sort" "sort"
"strings"
"time" "time"
) )
@ -20,6 +21,7 @@ var (
errInvalidActionType = NewExitError("ERROR invalid Action type. "+ errInvalidActionType = NewExitError("ERROR invalid Action type. "+
fmt.Sprintf("Must be `func(*Context`)` or `func(*Context) error). %s", contactSysadmin)+ fmt.Sprintf("Must be `func(*Context`)` or `func(*Context) error). %s", contactSysadmin)+
fmt.Sprintf("See %s", appActionDeprecationURL), 2) fmt.Sprintf("See %s", appActionDeprecationURL), 2)
ignoreFlagPrefix = "test." // this is to ignore test flags when adding flags from other packages
SuggestFlag SuggestFlagFunc = suggestFlag SuggestFlag SuggestFlagFunc = suggestFlag
SuggestCommand SuggestCommandFunc = suggestCommand SuggestCommand SuggestCommandFunc = suggestCommand
@ -43,6 +45,9 @@ type App struct {
Version string Version string
// Description of the program // Description of the program
Description string Description string
// DefaultCommand is the (optional) name of a command
// to run if no command names are passed as CLI arguments.
DefaultCommand string
// List of commands to execute // List of commands to execute
Commands []*Command Commands []*Command
// List of flags to parse // List of flags to parse
@ -74,6 +79,8 @@ type App struct {
CommandNotFound CommandNotFoundFunc CommandNotFound CommandNotFoundFunc
// Execute this function if a usage error occurs // Execute this function if a usage error occurs
OnUsageError OnUsageErrorFunc OnUsageError OnUsageErrorFunc
// Execute this function when an invalid flag is accessed from the context
InvalidFlagAccessHandler InvalidFlagAccessFunc
// Compilation date // Compilation date
Compiled time.Time Compiled time.Time
// List of all authors who contributed // List of all authors who contributed
@ -98,14 +105,26 @@ type App struct {
// cli.go uses text/template to render templates. You can // cli.go uses text/template to render templates. You can
// render custom help text by setting this variable. // render custom help text by setting this variable.
CustomAppHelpTemplate string CustomAppHelpTemplate string
// SliceFlagSeparator is used to customize the separator for SliceFlag, the default is ","
SliceFlagSeparator string
// DisableSliceFlagSeparator is used to disable SliceFlagSeparator, the default is false
DisableSliceFlagSeparator bool
// Boolean to enable short-option handling so user can combine several // Boolean to enable short-option handling so user can combine several
// single-character bool arguments into one // single-character bool arguments into one
// i.e. foobar -o -v -> foobar -ov // i.e. foobar -o -v -> foobar -ov
UseShortOptionHandling bool UseShortOptionHandling bool
// Enable suggestions for commands and flags // Enable suggestions for commands and flags
Suggest bool Suggest bool
// Allows global flags set by libraries which use flag.XXXVar(...) directly
// to be parsed through this library
AllowExtFlags bool
// Treat all flags as normal arguments if true
SkipFlagParsing bool
didSetup bool didSetup bool
separator separatorSpec
rootCommand *Command
} }
type SuggestFlagFunc func(flags []Flag, provided string, hideHelp bool) string type SuggestFlagFunc func(flags []Flag, provided string, hideHelp bool) string
@ -127,7 +146,6 @@ func compileTime() time.Time {
func NewApp() *App { func NewApp() *App {
return &App{ return &App{
Name: filepath.Base(os.Args[0]), Name: filepath.Base(os.Args[0]),
HelpName: filepath.Base(os.Args[0]),
Usage: "A new cli application", Usage: "A new cli application",
UsageText: "", UsageText: "",
BashComplete: DefaultAppComplete, BashComplete: DefaultAppComplete,
@ -189,13 +207,35 @@ func (a *App) Setup() {
a.ErrWriter = os.Stderr a.ErrWriter = os.Stderr
} }
if a.AllowExtFlags {
// add global flags added by other packages
flag.VisitAll(func(f *flag.Flag) {
// skip test flags
if !strings.HasPrefix(f.Name, ignoreFlagPrefix) {
a.Flags = append(a.Flags, &extFlag{f})
}
})
}
if len(a.SliceFlagSeparator) != 0 {
a.separator.customized = true
a.separator.sep = a.SliceFlagSeparator
}
if a.DisableSliceFlagSeparator {
a.separator.customized = true
a.separator.disabled = true
}
var newCommands []*Command var newCommands []*Command
for _, c := range a.Commands { for _, c := range a.Commands {
if c.HelpName == "" { cname := c.Name
c.HelpName = fmt.Sprintf("%s %s", a.HelpName, c.Name) if c.HelpName != "" {
cname = c.HelpName
} }
c.separator = a.separator
c.HelpName = fmt.Sprintf("%s %s", a.HelpName, cname)
c.flagCategories = newFlagCategoriesFromFlags(c.Flags) c.flagCategories = newFlagCategoriesFromFlags(c.Flags)
newCommands = append(newCommands, c) newCommands = append(newCommands, c)
} }
@ -221,20 +261,42 @@ func (a *App) Setup() {
} }
sort.Sort(a.categories.(*commandCategories)) sort.Sort(a.categories.(*commandCategories))
a.flagCategories = newFlagCategories() a.flagCategories = newFlagCategoriesFromFlags(a.Flags)
for _, fl := range a.Flags {
if cf, ok := fl.(CategorizableFlag); ok {
a.flagCategories.AddFlag(cf.GetCategory(), cf)
}
}
if a.Metadata == nil { if a.Metadata == nil {
a.Metadata = make(map[string]interface{}) a.Metadata = make(map[string]interface{})
} }
} }
func (a *App) newRootCommand() *Command {
return &Command{
Name: a.Name,
Usage: a.Usage,
UsageText: a.UsageText,
Description: a.Description,
ArgsUsage: a.ArgsUsage,
BashComplete: a.BashComplete,
Before: a.Before,
After: a.After,
Action: a.Action,
OnUsageError: a.OnUsageError,
Subcommands: a.Commands,
Flags: a.Flags,
flagCategories: a.flagCategories,
HideHelp: a.HideHelp,
HideHelpCommand: a.HideHelpCommand,
UseShortOptionHandling: a.UseShortOptionHandling,
HelpName: a.HelpName,
CustomHelpTemplate: a.CustomAppHelpTemplate,
categories: a.categories,
SkipFlagParsing: a.SkipFlagParsing,
isRoot: true,
separator: a.separator,
}
}
func (a *App) newFlagSet() (*flag.FlagSet, error) { func (a *App) newFlagSet() (*flag.FlagSet, error) {
return flagSet(a.Name, a.Flags) return flagSet(a.Name, a.Flags, a.separator)
} }
func (a *App) useShortOptionHandling() bool { func (a *App) useShortOptionHandling() bool {
@ -261,96 +323,20 @@ func (a *App) RunContext(ctx context.Context, arguments []string) (err error) {
// always appends the completion flag at the end of the command // always appends the completion flag at the end of the command
shellComplete, arguments := checkShellCompleteFlag(a, arguments) shellComplete, arguments := checkShellCompleteFlag(a, arguments)
set, err := a.newFlagSet() cCtx := NewContext(a, nil, &Context{Context: ctx})
if err != nil {
return err
}
err = parseIter(set, a, arguments[1:], shellComplete)
nerr := normalizeFlags(a.Flags, set)
cCtx := NewContext(a, set, &Context{Context: ctx})
if nerr != nil {
_, _ = fmt.Fprintln(a.Writer, nerr)
_ = ShowAppHelp(cCtx)
return nerr
}
cCtx.shellComplete = shellComplete cCtx.shellComplete = shellComplete
if checkCompletions(cCtx) { a.rootCommand = a.newRootCommand()
return nil cCtx.Command = a.rootCommand
}
if err != nil { return a.rootCommand.Run(cCtx, arguments...)
if a.OnUsageError != nil { }
err := a.OnUsageError(cCtx, err, false)
a.handleExitCoder(cCtx, err)
return err
}
_, _ = fmt.Fprintf(a.Writer, "%s %s\n\n", "Incorrect Usage.", err.Error())
if a.Suggest {
if suggestion, err := a.suggestFlagFromError(err, ""); err == nil {
fmt.Fprintf(a.Writer, suggestion)
}
}
_ = ShowAppHelp(cCtx)
return err
}
if !a.HideHelp && checkHelp(cCtx) { // This is a stub function to keep public API unchanged from old code
_ = ShowAppHelp(cCtx) //
return nil // Deprecated: use App.Run or App.RunContext
} func (a *App) RunAsSubcommand(ctx *Context) (err error) {
return a.RunContext(ctx.Context, ctx.Args().Slice())
if !a.HideVersion && checkVersion(cCtx) {
ShowVersion(cCtx)
return nil
}
cerr := cCtx.checkRequiredFlags(a.Flags)
if cerr != nil {
_ = ShowAppHelp(cCtx)
return cerr
}
if a.After != nil {
defer func() {
if afterErr := a.After(cCtx); afterErr != nil {
if err != nil {
err = newMultiError(err, afterErr)
} else {
err = afterErr
}
}
}()
}
if a.Before != nil {
beforeErr := a.Before(cCtx)
if beforeErr != nil {
a.handleExitCoder(cCtx, beforeErr)
err = beforeErr
return err
}
}
args := cCtx.Args()
if args.Present() {
name := args.First()
c := a.Command(name)
if c != nil {
return c.Run(cCtx)
}
}
if a.Action == nil {
a.Action = helpCommand.Action
}
// Run default Action
err = a.Action(cCtx)
a.handleExitCoder(cCtx, err)
return err
} }
func (a *App) suggestFlagFromError(err error, command string) (string, error) { func (a *App) suggestFlagFromError(err error, command string) (string, error) {
@ -360,15 +346,17 @@ func (a *App) suggestFlagFromError(err error, command string) (string, error) {
} }
flags := a.Flags flags := a.Flags
hideHelp := a.HideHelp
if command != "" { if command != "" {
cmd := a.Command(command) cmd := a.Command(command)
if cmd == nil { if cmd == nil {
return "", err return "", err
} }
flags = cmd.Flags flags = cmd.Flags
hideHelp = hideHelp || cmd.HideHelp
} }
suggestion := SuggestFlag(flags, flag, a.HideHelp) suggestion := SuggestFlag(flags, flag, hideHelp)
if len(suggestion) == 0 { if len(suggestion) == 0 {
return "", err return "", err
} }
@ -388,116 +376,6 @@ func (a *App) RunAndExitOnError() {
} }
} }
// RunAsSubcommand invokes the subcommand given the context, parses ctx.Args() to
// generate command-specific flags
func (a *App) RunAsSubcommand(ctx *Context) (err error) {
// Setup also handles HideHelp and HideHelpCommand
a.Setup()
var 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
set, err := a.newFlagSet()
if err != nil {
return err
}
err = parseIter(set, a, ctx.Args().Tail(), ctx.shellComplete)
nerr := normalizeFlags(a.Flags, set)
cCtx := NewContext(a, set, ctx)
if nerr != nil {
_, _ = fmt.Fprintln(a.Writer, nerr)
_, _ = fmt.Fprintln(a.Writer)
if len(a.Commands) > 0 {
_ = ShowSubcommandHelp(cCtx)
} else {
_ = ShowCommandHelp(ctx, cCtx.Args().First())
}
return nerr
}
if checkCompletions(cCtx) {
return nil
}
if err != nil {
if a.OnUsageError != nil {
err = a.OnUsageError(cCtx, err, true)
a.handleExitCoder(cCtx, err)
return err
}
_, _ = fmt.Fprintf(a.Writer, "%s %s\n\n", "Incorrect Usage.", err.Error())
if a.Suggest {
if suggestion, err := a.suggestFlagFromError(err, cCtx.Command.Name); err == nil {
fmt.Fprintf(a.Writer, suggestion)
}
}
_ = ShowSubcommandHelp(cCtx)
return err
}
if len(a.Commands) > 0 {
if checkSubcommandHelp(cCtx) {
return nil
}
} else {
if checkCommandHelp(ctx, cCtx.Args().First()) {
return nil
}
}
cerr := cCtx.checkRequiredFlags(a.Flags)
if cerr != nil {
_ = ShowSubcommandHelp(cCtx)
return cerr
}
if a.After != nil {
defer func() {
afterErr := a.After(cCtx)
if afterErr != nil {
a.handleExitCoder(cCtx, err)
if err != nil {
err = newMultiError(err, afterErr)
} else {
err = afterErr
}
}
}()
}
if a.Before != nil {
beforeErr := a.Before(cCtx)
if beforeErr != nil {
a.handleExitCoder(cCtx, beforeErr)
err = beforeErr
return err
}
}
args := cCtx.Args()
if args.Present() {
name := args.First()
c := a.Command(name)
if c != nil {
return c.Run(cCtx)
}
}
// Run default Action
err = a.Action(cCtx)
a.handleExitCoder(cCtx, err)
return err
}
// Command returns the named command on App. Returns nil if the command does not exist // Command returns the named command on App. Returns nil if the command does not exist
func (a *App) Command(name string) *Command { func (a *App) Command(name string) *Command {
for _, c := range a.Commands { for _, c := range a.Commands {
@ -570,6 +448,61 @@ func (a *App) handleExitCoder(cCtx *Context, err error) {
} }
} }
func (a *App) commandNames() []string {
var cmdNames []string
for _, cmd := range a.Commands {
cmdNames = append(cmdNames, cmd.Names()...)
}
return cmdNames
}
func (a *App) validCommandName(checkCmdName string) bool {
valid := false
allCommandNames := a.commandNames()
for _, cmdName := range allCommandNames {
if checkCmdName == cmdName {
valid = true
break
}
}
return valid
}
func (a *App) argsWithDefaultCommand(oldArgs Args) Args {
if a.DefaultCommand != "" {
rawArgs := append([]string{a.DefaultCommand}, oldArgs.Slice()...)
newArgs := args(rawArgs)
return &newArgs
}
return oldArgs
}
func runFlagActions(c *Context, fs []Flag) error {
for _, f := range fs {
isSet := false
for _, name := range f.Names() {
if c.IsSet(name) {
isSet = true
break
}
}
if isSet {
if af, ok := f.(ActionableFlag); ok {
if err := af.RunAction(c); err != nil {
return err
}
}
}
}
return nil
}
// Author represents someone who has contributed to a cli project. // Author represents someone who has contributed to a cli project.
type Author struct { type Author struct {
Name string // The Authors name Name string // The Authors name
@ -602,3 +535,15 @@ func HandleAction(action interface{}, cCtx *Context) (err error) {
return errInvalidActionType return errInvalidActionType
} }
func checkStringSliceIncludes(want string, sSlice []string) bool {
found := false
for _, s := range sSlice {
if want == s {
found = true
break
}
}
return found
}

View File

@ -100,9 +100,24 @@ func newFlagCategories() FlagCategories {
func newFlagCategoriesFromFlags(fs []Flag) FlagCategories { func newFlagCategoriesFromFlags(fs []Flag) FlagCategories {
fc := newFlagCategories() fc := newFlagCategories()
var categorized bool
for _, fl := range fs { for _, fl := range fs {
if cf, ok := fl.(CategorizableFlag); ok { if cf, ok := fl.(CategorizableFlag); ok {
fc.AddFlag(cf.GetCategory(), cf) if cat := cf.GetCategory(); cat != "" {
fc.AddFlag(cat, cf)
categorized = true
}
}
}
if categorized == true {
for _, fl := range fs {
if cf, ok := fl.(CategorizableFlag); ok {
if cf.GetCategory() == "" {
fc.AddFlag("", fl)
}
}
} }
} }

View File

@ -1,23 +1,25 @@
// Package cli provides a minimal framework for creating and organizing command line // 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 // Go applications. cli is designed to be easy to understand and write, the most simple
// cli application can be written as follows: // cli application can be written as follows:
// func main() { //
// (&cli.App{}).Run(os.Args) // func main() {
// } // (&cli.App{}).Run(os.Args)
// }
// //
// Of course this application does not do much, so let's make this an actual application: // Of course this application does not do much, so let's make this an actual application:
// func main() {
// app := &cli.App{
// Name: "greet",
// Usage: "say a greeting",
// Action: func(c *cli.Context) error {
// fmt.Println("Greetings")
// return nil
// },
// }
// //
// app.Run(os.Args) // func main() {
// } // app := &cli.App{
// Name: "greet",
// Usage: "say a greeting",
// Action: func(c *cli.Context) error {
// fmt.Println("Greetings")
// return nil
// },
// }
//
// app.Run(os.Args)
// }
package cli package cli
//go:generate go run internal/genflags/cmd/genflags/main.go //go:generate make -C cmd/urfave-cli-genflags run

View File

@ -3,6 +3,7 @@ package cli
import ( import (
"flag" "flag"
"fmt" "fmt"
"reflect"
"sort" "sort"
"strings" "strings"
) )
@ -62,6 +63,14 @@ type Command struct {
// cli.go uses text/template to render templates. You can // cli.go uses text/template to render templates. You can
// render custom help text by setting this variable. // render custom help text by setting this variable.
CustomHelpTemplate string CustomHelpTemplate string
// categories contains the categorized commands and is populated on app startup
categories CommandCategories
// if this is a root "special" command
isRoot bool
separator separatorSpec
} }
type Commands []*Command type Commands []*Command
@ -89,10 +98,21 @@ func (c *Command) FullName() string {
return strings.Join(c.commandNamePath, " ") return strings.Join(c.commandNamePath, " ")
} }
// Run invokes the command given the context, parses ctx.Args() to generate command-specific flags func (cmd *Command) Command(name string) *Command {
func (c *Command) Run(ctx *Context) (err error) { for _, c := range cmd.Subcommands {
if len(c.Subcommands) > 0 { if c.HasName(name) {
return c.startApp(ctx) return c
}
}
return nil
}
func (c *Command) setup(ctx *Context) {
if c.Command(helpCommand.Name) == nil && !c.HideHelp {
if !c.HideHelpCommand {
c.Subcommands = append(c.Subcommands, helpCommand)
}
} }
if !c.HideHelp && HelpFlag != nil { if !c.HideHelp && HelpFlag != nil {
@ -104,42 +124,72 @@ func (c *Command) Run(ctx *Context) (err error) {
c.UseShortOptionHandling = true c.UseShortOptionHandling = true
} }
set, err := c.parseFlags(ctx.Args(), ctx.shellComplete) c.categories = newCommandCategories()
for _, command := range c.Subcommands {
c.categories.AddCommand(command.Category, command)
}
sort.Sort(c.categories.(*commandCategories))
cCtx := NewContext(ctx.App, set, ctx) var newCmds []*Command
cCtx.Command = c for _, scmd := range c.Subcommands {
if checkCommandCompletions(cCtx, c.Name) { if scmd.HelpName == "" {
scmd.HelpName = fmt.Sprintf("%s %s", c.HelpName, scmd.Name)
}
newCmds = append(newCmds, scmd)
}
c.Subcommands = newCmds
if c.BashComplete == nil {
c.BashComplete = DefaultCompleteWithFlags(c)
}
}
func (c *Command) Run(cCtx *Context, arguments ...string) (err error) {
if !c.isRoot {
c.setup(cCtx)
}
a := args(arguments)
set, err := c.parseFlags(&a, cCtx.shellComplete)
cCtx.flagSet = set
if checkCompletions(cCtx) {
return nil return nil
} }
if err != nil { if err != nil {
if c.OnUsageError != nil { if c.OnUsageError != nil {
err = c.OnUsageError(cCtx, err, false) err = c.OnUsageError(cCtx, err, !c.isRoot)
cCtx.App.handleExitCoder(cCtx, err) cCtx.App.handleExitCoder(cCtx, err)
return err return err
} }
_, _ = fmt.Fprintln(cCtx.App.Writer, "Incorrect Usage:", err.Error()) _, _ = fmt.Fprintf(cCtx.App.Writer, "%s %s\n\n", "Incorrect Usage:", err.Error())
_, _ = fmt.Fprintln(cCtx.App.Writer) if cCtx.App.Suggest {
if ctx.App.Suggest { if suggestion, err := c.suggestFlagFromError(err, ""); err == nil {
if suggestion, err := ctx.App.suggestFlagFromError(err, c.Name); err == nil { fmt.Fprintf(cCtx.App.Writer, "%s", suggestion)
fmt.Fprintf(cCtx.App.Writer, suggestion) }
}
if !c.HideHelp {
if c.isRoot {
_ = ShowAppHelp(cCtx)
} else {
_ = ShowCommandHelp(cCtx.parentContext, c.Name)
} }
} }
_ = ShowCommandHelp(cCtx, c.Name)
return err return err
} }
if checkCommandHelp(cCtx, c.Name) { if checkHelp(cCtx) {
return helpCommand.Action(cCtx)
}
if c.isRoot && !cCtx.App.HideVersion && checkVersion(cCtx) {
ShowVersion(cCtx)
return nil return nil
} }
cerr := cCtx.checkRequiredFlags(c.Flags) if c.After != nil && !cCtx.shellComplete {
if cerr != nil {
_ = ShowCommandHelp(cCtx, c.Name)
return cerr
}
if c.After != nil {
defer func() { defer func() {
afterErr := c.After(cCtx) afterErr := c.After(cCtx)
if afterErr != nil { if afterErr != nil {
@ -153,35 +203,112 @@ func (c *Command) Run(ctx *Context) (err error) {
}() }()
} }
if c.Before != nil { cerr := cCtx.checkRequiredFlags(c.Flags)
err = c.Before(cCtx) if cerr != nil {
if err != nil { _ = helpCommand.Action(cCtx)
cCtx.App.handleExitCoder(cCtx, err) return cerr
}
if c.Before != nil && !cCtx.shellComplete {
beforeErr := c.Before(cCtx)
if beforeErr != nil {
cCtx.App.handleExitCoder(cCtx, beforeErr)
err = beforeErr
return err return err
} }
} }
if c.Action == nil { if err = runFlagActions(cCtx, c.Flags); err != nil {
c.Action = helpSubcommand.Action return err
}
var cmd *Command
args := cCtx.Args()
if args.Present() {
name := args.First()
cmd = c.Command(name)
if cmd == nil {
hasDefault := cCtx.App.DefaultCommand != ""
isFlagName := checkStringSliceIncludes(name, cCtx.FlagNames())
var (
isDefaultSubcommand = false
defaultHasSubcommands = false
)
if hasDefault {
dc := cCtx.App.Command(cCtx.App.DefaultCommand)
defaultHasSubcommands = len(dc.Subcommands) > 0
for _, dcSub := range dc.Subcommands {
if checkStringSliceIncludes(name, dcSub.Names()) {
isDefaultSubcommand = true
break
}
}
}
if isFlagName || (hasDefault && (defaultHasSubcommands && isDefaultSubcommand)) {
argsWithDefault := cCtx.App.argsWithDefaultCommand(args)
if !reflect.DeepEqual(args, argsWithDefault) {
cmd = cCtx.App.rootCommand.Command(argsWithDefault.First())
}
}
}
} else if c.isRoot && cCtx.App.DefaultCommand != "" {
if dc := cCtx.App.Command(cCtx.App.DefaultCommand); dc != c {
cmd = dc
}
}
if cmd != nil {
newcCtx := NewContext(cCtx.App, nil, cCtx)
newcCtx.Command = cmd
return cmd.Run(newcCtx, cCtx.Args().Slice()...)
}
if c.Action == nil {
c.Action = helpCommand.Action
} }
cCtx.Command = c
err = c.Action(cCtx) err = c.Action(cCtx)
if err != nil { cCtx.App.handleExitCoder(cCtx, err)
cCtx.App.handleExitCoder(cCtx, err)
}
return err return err
} }
func (c *Command) newFlagSet() (*flag.FlagSet, error) { func (c *Command) newFlagSet() (*flag.FlagSet, error) {
return flagSet(c.Name, c.Flags) return flagSet(c.Name, c.Flags, c.separator)
} }
func (c *Command) useShortOptionHandling() bool { func (c *Command) useShortOptionHandling() bool {
return c.UseShortOptionHandling return c.UseShortOptionHandling
} }
func (c *Command) suggestFlagFromError(err error, command string) (string, error) {
flag, parseErr := flagFromError(err)
if parseErr != nil {
return "", err
}
flags := c.Flags
hideHelp := c.HideHelp
if command != "" {
cmd := c.Command(command)
if cmd == nil {
return "", err
}
flags = cmd.Flags
hideHelp = hideHelp || cmd.HideHelp
}
suggestion := SuggestFlag(flags, flag, hideHelp)
if len(suggestion) == 0 {
return "", err
}
return fmt.Sprintf(SuggestDidYouMeanTemplate, suggestion) + "\n\n", nil
}
func (c *Command) parseFlags(args Args, shellComplete bool) (*flag.FlagSet, error) { func (c *Command) parseFlags(args Args, shellComplete bool) (*flag.FlagSet, error) {
set, err := c.newFlagSet() set, err := c.newFlagSet()
if err != nil { if err != nil {
@ -220,77 +347,38 @@ func (c *Command) HasName(name string) bool {
return false return false
} }
func (c *Command) startApp(ctx *Context) error { // VisibleCategories returns a slice of categories and commands that are
app := &App{ // Hidden=false
Metadata: ctx.App.Metadata, func (c *Command) VisibleCategories() []CommandCategory {
Name: fmt.Sprintf("%s %s", ctx.App.Name, c.Name), ret := []CommandCategory{}
for _, category := range c.categories.Categories() {
if visible := func() CommandCategory {
if len(category.VisibleCommands()) > 0 {
return category
}
return nil
}(); visible != nil {
ret = append(ret, visible)
}
} }
return ret
}
if c.HelpName == "" { // VisibleCommands returns a slice of the Commands with Hidden=false
app.HelpName = c.HelpName func (c *Command) VisibleCommands() []*Command {
} else { var ret []*Command
app.HelpName = app.Name
}
app.Usage = c.Usage
app.UsageText = c.UsageText
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.HideHelpCommand = c.HideHelpCommand
app.Version = ctx.App.Version
app.HideVersion = true
app.Compiled = ctx.App.Compiled
app.Reader = ctx.App.Reader
app.Writer = ctx.App.Writer
app.ErrWriter = ctx.App.ErrWriter
app.ExitErrHandler = ctx.App.ExitErrHandler
app.UseShortOptionHandling = ctx.App.UseShortOptionHandling
app.Suggest = ctx.App.Suggest
app.categories = newCommandCategories()
for _, command := range c.Subcommands { for _, command := range c.Subcommands {
app.categories.AddCommand(command.Category, command) if !command.Hidden {
ret = append(ret, command)
}
} }
return ret
sort.Sort(app.categories.(*commandCategories))
// 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)
} }
// VisibleFlagCategories returns a slice containing all the visible flag categories with the flags they contain // VisibleFlagCategories returns a slice containing all the visible flag categories with the flags they contain
func (c *Command) VisibleFlagCategories() []VisibleFlagCategory { func (c *Command) VisibleFlagCategories() []VisibleFlagCategory {
if c.flagCategories == nil { if c.flagCategories == nil {
return []VisibleFlagCategory{} c.flagCategories = newFlagCategoriesFromFlags(c.Flags)
} }
return c.flagCategories.VisibleCategories() return c.flagCategories.VisibleCategories()
} }

View File

@ -3,6 +3,7 @@ package cli
import ( import (
"context" "context"
"flag" "flag"
"fmt"
"strings" "strings"
) )
@ -46,7 +47,11 @@ func (cCtx *Context) NumFlags() int {
// Set sets a context flag to a value. // Set sets a context flag to a value.
func (cCtx *Context) Set(name, value string) error { func (cCtx *Context) Set(name, value string) error {
return cCtx.flagSet.Set(name, value) if fs := cCtx.lookupFlagSet(name); fs != nil {
return fs.Set(name, value)
}
return fmt.Errorf("no such flag -%s", name)
} }
// IsSet determines if the flag was actually set // IsSet determines if the flag was actually set
@ -77,7 +82,27 @@ func (cCtx *Context) IsSet(name string) bool {
func (cCtx *Context) LocalFlagNames() []string { func (cCtx *Context) LocalFlagNames() []string {
var names []string var names []string
cCtx.flagSet.Visit(makeFlagNameVisitor(&names)) cCtx.flagSet.Visit(makeFlagNameVisitor(&names))
return names // Check the flags which have been set via env or file
if cCtx.Command != nil && cCtx.Command.Flags != nil {
for _, f := range cCtx.Command.Flags {
if f.IsSet() {
names = append(names, f.Names()...)
}
}
}
// Sort out the duplicates since flag could be set via multiple
// paths
m := map[string]struct{}{}
var unames []string
for _, name := range names {
if _, ok := m[name]; !ok {
m[name] = struct{}{}
unames = append(unames, name)
}
}
return unames
} }
// FlagNames returns a slice of flag names used by the this context and all of // FlagNames returns a slice of flag names used by the this context and all of
@ -85,7 +110,7 @@ func (cCtx *Context) LocalFlagNames() []string {
func (cCtx *Context) FlagNames() []string { func (cCtx *Context) FlagNames() []string {
var names []string var names []string
for _, pCtx := range cCtx.Lineage() { for _, pCtx := range cCtx.Lineage() {
pCtx.flagSet.Visit(makeFlagNameVisitor(&names)) names = append(names, pCtx.LocalFlagNames()...)
} }
return names return names
} }
@ -102,6 +127,16 @@ func (cCtx *Context) Lineage() []*Context {
return lineage return lineage
} }
// Count returns the num of occurences of this flag
func (cCtx *Context) Count(name string) int {
if fs := cCtx.lookupFlagSet(name); fs != nil {
if cf, ok := fs.Lookup(name).Value.(Countable); ok {
return cf.Count()
}
}
return 0
}
// Value returns the value of the flag corresponding to `name` // Value returns the value of the flag corresponding to `name`
func (cCtx *Context) Value(name string) interface{} { func (cCtx *Context) Value(name string) interface{} {
if fs := cCtx.lookupFlagSet(name); fs != nil { if fs := cCtx.lookupFlagSet(name); fs != nil {
@ -158,7 +193,7 @@ func (cCtx *Context) lookupFlagSet(name string) *flag.FlagSet {
return c.flagSet return c.flagSet
} }
} }
cCtx.onInvalidFlag(name)
return nil return nil
} }
@ -170,9 +205,7 @@ func (cCtx *Context) checkRequiredFlags(flags []Flag) requiredFlagsErr {
var flagName string var flagName string
for _, key := range f.Names() { for _, key := range f.Names() {
if len(key) > 1 { flagName = key
flagName = key
}
if cCtx.IsSet(strings.TrimSpace(key)) { if cCtx.IsSet(strings.TrimSpace(key)) {
flagPresent = true flagPresent = true
@ -192,6 +225,16 @@ func (cCtx *Context) checkRequiredFlags(flags []Flag) requiredFlagsErr {
return nil return nil
} }
func (cCtx *Context) onInvalidFlag(name string) {
for cCtx != nil {
if cCtx.App != nil && cCtx.App.InvalidFlagAccessHandler != nil {
cCtx.App.InvalidFlagAccessHandler(cCtx, name)
break
}
cCtx = cCtx.parentContext
}
}
func makeFlagNameVisitor(names *[]string) func(*flag.Flag) { func makeFlagNameVisitor(names *[]string) func(*flag.Flag) {
return func(f *flag.Flag) { return func(f *flag.Flag) {
nameParts := strings.Split(f.Name, ",") nameParts := strings.Split(f.Name, ",")

View File

@ -83,7 +83,7 @@ type ExitCoder interface {
type exitError struct { type exitError struct {
exitCode int exitCode int
message interface{} err error
} }
// NewExitError calls Exit to create a new ExitCoder. // NewExitError calls Exit to create a new ExitCoder.
@ -98,23 +98,38 @@ func NewExitError(message interface{}, exitCode int) ExitCoder {
// //
// This is the simplest way to trigger a non-zero exit code for an App without // This is the simplest way to trigger a non-zero exit code for an App without
// having to call os.Exit manually. During testing, this behavior can be avoided // having to call os.Exit manually. During testing, this behavior can be avoided
// by overiding the ExitErrHandler function on an App or the package-global // by overriding the ExitErrHandler function on an App or the package-global
// OsExiter function. // OsExiter function.
func Exit(message interface{}, exitCode int) ExitCoder { func Exit(message interface{}, exitCode int) ExitCoder {
var err error
switch e := message.(type) {
case ErrorFormatter:
err = fmt.Errorf("%+v", message)
case error:
err = e
default:
err = fmt.Errorf("%+v", message)
}
return &exitError{ return &exitError{
message: message, err: err,
exitCode: exitCode, exitCode: exitCode,
} }
} }
func (ee *exitError) Error() string { func (ee *exitError) Error() string {
return fmt.Sprintf("%v", ee.message) return ee.err.Error()
} }
func (ee *exitError) ExitCode() int { func (ee *exitError) ExitCode() int {
return ee.exitCode return ee.exitCode
} }
func (ee *exitError) Unwrap() error {
return ee.err
}
// HandleExitCoder handles errors implementing ExitCoder by printing their // HandleExitCoder handles errors implementing ExitCoder by printing their
// message and calling OsExiter with the given exit code. // message and calling OsExiter with the given exit code.
// //

View File

@ -98,7 +98,7 @@ func (a *App) prepareFishCommands(commands []*Command, allCommands *[]string, pr
a.prepareFishFlags(command.VisibleFlags(), command.Names())..., a.prepareFishFlags(command.VisibleFlags(), command.Names())...,
) )
// recursevly iterate subcommands // recursively iterate subcommands
if len(command.Subcommands) > 0 { if len(command.Subcommands) > 0 {
completions = append( completions = append(
completions, completions,

View File

@ -1,50 +1,131 @@
# NOTE: this file is used by the tool defined in # NOTE: this file is used by the tool defined in
# ./internal/genflags/cmd/genflags/main.go which uses the # ./cmd/urfave-cli-genflags/main.go which uses the
# `genflags.Spec` type that maps to this file structure. # `Spec` type that maps to this file structure.
flag_types: flag_types:
bool: {} bool:
float64: {}
int64: {}
int: {}
time.Duration: {}
uint64: {}
uint: {}
string:
struct_fields: struct_fields:
- { name: TakesFile, type: bool } - name: Count
Generic: type: int
pointer: true
- name: DisableDefaultText
type: bool
- name: Action
type: "func(*Context, bool) error"
float64:
struct_fields: struct_fields:
- { name: TakesFile, type: bool } - name: Action
Path: type: "func(*Context, float64) error"
struct_fields:
- { name: TakesFile, type: bool }
Float64Slice: Float64Slice:
value_pointer: true value_pointer: true
skip_interfaces: skip_interfaces:
- fmt.Stringer - fmt.Stringer
Int64Slice: struct_fields:
value_pointer: true - name: separator
skip_interfaces: type: separatorSpec
- fmt.Stringer - name: Action
type: "func(*Context, []float64) error"
int:
struct_fields:
- name: Base
type: int
- name: Action
type: "func(*Context, int) error"
IntSlice: IntSlice:
value_pointer: true value_pointer: true
skip_interfaces: skip_interfaces:
- fmt.Stringer - fmt.Stringer
struct_fields:
- name: separator
type: separatorSpec
- name: Action
type: "func(*Context, []int) error"
int64:
struct_fields:
- name: Base
type: int
- name: Action
type: "func(*Context, int64) error"
Int64Slice:
value_pointer: true
skip_interfaces:
- fmt.Stringer
struct_fields:
- name: separator
type: separatorSpec
- name: Action
type: "func(*Context, []int64) error"
uint:
struct_fields:
- name: Base
type: int
- name: Action
type: "func(*Context, uint) error"
UintSlice:
value_pointer: true
skip_interfaces:
- fmt.Stringer
struct_fields:
- name: separator
type: separatorSpec
- name: Action
type: "func(*Context, []uint) error"
uint64:
struct_fields:
- name: Base
type: int
- name: Action
type: "func(*Context, uint64) error"
Uint64Slice:
value_pointer: true
skip_interfaces:
- fmt.Stringer
struct_fields:
- name: separator
type: separatorSpec
- name: Action
type: "func(*Context, []uint64) error"
string:
struct_fields:
- name: TakesFile
type: bool
- name: Action
type: "func(*Context, string) error"
StringSlice: StringSlice:
value_pointer: true value_pointer: true
skip_interfaces: skip_interfaces:
- fmt.Stringer - fmt.Stringer
struct_fields: struct_fields:
- { name: TakesFile, type: bool } - name: separator
type: separatorSpec
- name: TakesFile
type: bool
- name: Action
type: "func(*Context, []string) error"
- name: KeepSpace
type: bool
time.Duration:
struct_fields:
- name: Action
type: "func(*Context, time.Duration) error"
Timestamp: Timestamp:
value_pointer: true value_pointer: true
struct_fields: struct_fields:
- { name: Layout, type: string } - name: Layout
type: string
# TODO: enable UintSlice - name: Timezone
# UintSlice: {} type: "*time.Location"
# TODO: enable Uint64Slice once #1334 lands - name: Action
# Uint64Slice: {} type: "func(*Context, *time.Time) error"
Generic:
no_destination_pointer: true
struct_fields:
- name: TakesFile
type: bool
- name: Action
type: "func(*Context, interface{}) error"
Path:
struct_fields:
- name: TakesFile
type: bool
- name: Action
type: "func(*Context, Path) error"

View File

@ -4,10 +4,10 @@ import (
"errors" "errors"
"flag" "flag"
"fmt" "fmt"
"io/ioutil" "io"
"os"
"regexp" "regexp"
"runtime" "runtime"
"strconv"
"strings" "strings"
"syscall" "syscall"
"time" "time"
@ -15,6 +15,11 @@ import (
const defaultPlaceholder = "value" const defaultPlaceholder = "value"
const (
defaultSliceFlagSeparator = ","
disableSliceFlagSeparator = false
)
var ( var (
slPfx = fmt.Sprintf("sl:::%d:::", time.Now().UTC().UnixNano()) slPfx = fmt.Sprintf("sl:::%d:::", time.Now().UTC().UnixNano())
@ -29,18 +34,20 @@ var BashCompletionFlag Flag = &BoolFlag{
// VersionFlag prints the version for the application // VersionFlag prints the version for the application
var VersionFlag Flag = &BoolFlag{ var VersionFlag Flag = &BoolFlag{
Name: "version", Name: "version",
Aliases: []string{"v"}, Aliases: []string{"v"},
Usage: "print the version", Usage: "print the version",
DisableDefaultText: true,
} }
// HelpFlag prints the help for all commands and subcommands. // HelpFlag prints the help for all commands and subcommands.
// Set to nil to disable the flag. The subcommand // Set to nil to disable the flag. The subcommand
// will still be added unless HideHelp or HideHelpCommand is set to true. // will still be added unless HideHelp or HideHelpCommand is set to true.
var HelpFlag Flag = &BoolFlag{ var HelpFlag Flag = &BoolFlag{
Name: "help", Name: "help",
Aliases: []string{"h"}, Aliases: []string{"h"},
Usage: "show help", Usage: "show help",
DisableDefaultText: true,
} }
// FlagStringer converts a flag definition to a string. This is used by help // FlagStringer converts a flag definition to a string. This is used by help
@ -84,6 +91,12 @@ func (f FlagsByName) Swap(i, j int) {
f[i], f[j] = f[j], f[i] f[i], f[j] = f[j], f[i]
} }
// ActionableFlag is an interface that wraps Flag interface and RunAction operation.
type ActionableFlag interface {
Flag
RunAction(*Context) error
}
// Flag is a common interface related to parsing flags in cli. // Flag is a common interface related to parsing flags in cli.
// For more advanced flag parsing techniques, it is recommended that // For more advanced flag parsing techniques, it is recommended that
// this interface be implemented. // this interface be implemented.
@ -124,6 +137,14 @@ type DocGenerationFlag interface {
GetEnvVars() []string GetEnvVars() []string
} }
// DocGenerationSliceFlag extends DocGenerationFlag for slice-based flags.
type DocGenerationSliceFlag interface {
DocGenerationFlag
// IsSliceFlag returns true for flags that can be given multiple times.
IsSliceFlag() bool
}
// VisibleFlag is an interface that allows to check if a flag is visible // VisibleFlag is an interface that allows to check if a flag is visible
type VisibleFlag interface { type VisibleFlag interface {
Flag Flag
@ -140,15 +161,24 @@ type CategorizableFlag interface {
GetCategory() string GetCategory() string
} }
func flagSet(name string, flags []Flag) (*flag.FlagSet, error) { // Countable is an interface to enable detection of flag values which support
// repetitive flags
type Countable interface {
Count() int
}
func flagSet(name string, flags []Flag, spec separatorSpec) (*flag.FlagSet, error) {
set := flag.NewFlagSet(name, flag.ContinueOnError) set := flag.NewFlagSet(name, flag.ContinueOnError)
for _, f := range flags { for _, f := range flags {
if c, ok := f.(customizedSeparator); ok {
c.WithSeparatorSpec(spec)
}
if err := f.Apply(set); err != nil { if err := f.Apply(set); err != nil {
return nil, err return nil, err
} }
} }
set.SetOutput(ioutil.Discard) set.SetOutput(io.Discard)
return set, nil return set, nil
} }
@ -249,19 +279,23 @@ func prefixedNames(names []string, placeholder string) string {
return prefixed return prefixed
} }
func envFormat(envVars []string, prefix, sep, suffix string) string {
if len(envVars) > 0 {
return fmt.Sprintf(" [%s%s%s]", prefix, strings.Join(envVars, sep), suffix)
}
return ""
}
func defaultEnvFormat(envVars []string) string {
return envFormat(envVars, "$", ", $", "")
}
func withEnvHint(envVars []string, str string) string { func withEnvHint(envVars []string, str string) string {
envText := "" envText := ""
if len(envVars) > 0 { if runtime.GOOS != "windows" || os.Getenv("PSHOME") != "" {
prefix := "$" envText = defaultEnvFormat(envVars)
suffix := "" } else {
sep := ", $" envText = envFormat(envVars, "%", "%, %", "%")
if runtime.GOOS == "windows" {
prefix = "%"
suffix = "%"
sep = "%, %"
}
envText = fmt.Sprintf(" [%s%s%s]", prefix, strings.Join(envVars, sep), suffix)
} }
return str + envText return str + envText
} }
@ -308,80 +342,24 @@ func stringifyFlag(f Flag) string {
defaultValueString := "" defaultValueString := ""
if s := df.GetDefaultText(); s != "" { // set default text for all flags except bool flags
defaultValueString = fmt.Sprintf(formatDefault("%s"), s) // for bool flags display default text if DisableDefaultText is not
// set
if bf, ok := f.(*BoolFlag); !ok || !bf.DisableDefaultText {
if s := df.GetDefaultText(); s != "" {
defaultValueString = fmt.Sprintf(formatDefault("%s"), s)
}
} }
usageWithDefault := strings.TrimSpace(usage + defaultValueString) usageWithDefault := strings.TrimSpace(usage + defaultValueString)
return withEnvHint(df.GetEnvVars(), pn := prefixedNames(df.Names(), placeholder)
fmt.Sprintf("%s\t%s", prefixedNames(df.Names(), placeholder), usageWithDefault)) sliceFlag, ok := f.(DocGenerationSliceFlag)
} if ok && sliceFlag.IsSliceFlag() {
pn = pn + " [ " + pn + " ]"
func stringifyIntSliceFlag(f *IntSliceFlag) string {
var defaultVals []string
if f.Value != nil && len(f.Value.Value()) > 0 {
for _, i := range f.Value.Value() {
defaultVals = append(defaultVals, strconv.Itoa(i))
}
} }
return stringifySliceFlag(f.Usage, f.Names(), defaultVals) return withEnvHint(df.GetEnvVars(), fmt.Sprintf("%s\t%s", pn, usageWithDefault))
}
func stringifyInt64SliceFlag(f *Int64SliceFlag) string {
var defaultVals []string
if f.Value != nil && len(f.Value.Value()) > 0 {
for _, i := range f.Value.Value() {
defaultVals = append(defaultVals, strconv.FormatInt(i, 10))
}
}
return stringifySliceFlag(f.Usage, f.Names(), defaultVals)
}
func stringifyFloat64SliceFlag(f *Float64SliceFlag) string {
var defaultVals []string
if f.Value != nil && len(f.Value.Value()) > 0 {
for _, i := range f.Value.Value() {
defaultVals = append(defaultVals, strings.TrimRight(strings.TrimRight(fmt.Sprintf("%f", i), "0"), "."))
}
}
return stringifySliceFlag(f.Usage, f.Names(), defaultVals)
}
func stringifyStringSliceFlag(f *StringSliceFlag) string {
var defaultVals []string
if f.Value != nil && len(f.Value.Value()) > 0 {
for _, s := range f.Value.Value() {
if len(s) > 0 {
defaultVals = append(defaultVals, strconv.Quote(s))
}
}
}
return stringifySliceFlag(f.Usage, f.Names(), defaultVals)
}
func stringifySliceFlag(usage string, names, defaultVals []string) string {
placeholder, usage := unquoteUsage(usage)
if placeholder == "" {
placeholder = defaultPlaceholder
}
defaultVal := ""
if len(defaultVals) > 0 {
defaultVal = fmt.Sprintf(formatDefault("%s"), strings.Join(defaultVals, ", "))
}
usageWithDefault := strings.TrimSpace(fmt.Sprintf("%s%s", usage, defaultVal))
multiInputString := "(accepts multiple inputs)"
if usageWithDefault != "" {
multiInputString = "\t" + multiInputString
}
return fmt.Sprintf("%s\t%s%s", prefixedNames(names, placeholder), usageWithDefault, multiInputString)
} }
func hasFlag(flags []Flag, fl Flag) bool { func hasFlag(flags []Flag, fl Flag) bool {
@ -406,7 +384,7 @@ func flagFromEnvOrFile(envVars []string, filePath string) (value string, fromWhe
} }
for _, fileVar := range strings.Split(filePath, ",") { for _, fileVar := range strings.Split(filePath, ",") {
if fileVar != "" { if fileVar != "" {
if data, err := ioutil.ReadFile(fileVar); err == nil { if data, err := os.ReadFile(fileVar); err == nil {
return string(data), fmt.Sprintf("file %q", filePath), true return string(data), fmt.Sprintf("file %q", filePath), true
} }
} }
@ -414,6 +392,28 @@ func flagFromEnvOrFile(envVars []string, filePath string) (value string, fromWhe
return "", "", false return "", "", false
} }
func flagSplitMultiValues(val string) []string { type customizedSeparator interface {
return strings.Split(val, ",") WithSeparatorSpec(separatorSpec)
}
type separatorSpec struct {
sep string
disabled bool
customized bool
}
func (s separatorSpec) flagSplitMultiValues(val string) []string {
var (
disabled bool = s.disabled
sep string = s.sep
)
if !s.customized {
disabled = disableSliceFlagSeparator
sep = defaultSliceFlagSeparator
}
if disabled {
return []string{val}
}
return strings.Split(val, sep)
} }

View File

@ -1,11 +1,63 @@
package cli package cli
import ( import (
"errors"
"flag" "flag"
"fmt" "fmt"
"strconv" "strconv"
) )
// boolValue needs to implement the boolFlag internal interface in flag
// to be able to capture bool fields and values
//
// type boolFlag interface {
// Value
// IsBoolFlag() bool
// }
type boolValue struct {
destination *bool
count *int
}
func newBoolValue(val bool, p *bool, count *int) *boolValue {
*p = val
return &boolValue{
destination: p,
count: count,
}
}
func (b *boolValue) Set(s string) error {
v, err := strconv.ParseBool(s)
if err != nil {
err = errors.New("parse error")
return err
}
*b.destination = v
if b.count != nil {
*b.count = *b.count + 1
}
return err
}
func (b *boolValue) Get() interface{} { return *b.destination }
func (b *boolValue) String() string {
if b.destination != nil {
return strconv.FormatBool(*b.destination)
}
return strconv.FormatBool(false)
}
func (b *boolValue) IsBoolFlag() bool { return true }
func (b *boolValue) Count() int {
if b.count != nil {
return *b.count
}
return 0
}
// TakesValue returns true of the flag takes a value, otherwise false // TakesValue returns true of the flag takes a value, otherwise false
func (f *BoolFlag) TakesValue() bool { func (f *BoolFlag) TakesValue() bool {
return false return false
@ -32,7 +84,7 @@ func (f *BoolFlag) GetDefaultText() string {
if f.DefaultText != "" { if f.DefaultText != "" {
return f.DefaultText return f.DefaultText
} }
return fmt.Sprintf("%v", f.Value) return fmt.Sprintf("%v", f.defaultValue)
} }
// GetEnvVars returns the env vars for this flag // GetEnvVars returns the env vars for this flag
@ -40,8 +92,20 @@ func (f *BoolFlag) GetEnvVars() []string {
return f.EnvVars return f.EnvVars
} }
// RunAction executes flag action if set
func (f *BoolFlag) RunAction(c *Context) error {
if f.Action != nil {
return f.Action(c, c.Bool(f.Name))
}
return nil
}
// Apply populates the flag given the flag set and environment // Apply populates the flag given the flag set and environment
func (f *BoolFlag) Apply(set *flag.FlagSet) error { func (f *BoolFlag) Apply(set *flag.FlagSet) error {
// set default value so that environment wont be able to overwrite it
f.defaultValue = f.Value
if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found { if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {
if val != "" { if val != "" {
valBool, err := strconv.ParseBool(val) valBool, err := strconv.ParseBool(val)
@ -51,16 +115,28 @@ func (f *BoolFlag) Apply(set *flag.FlagSet) error {
} }
f.Value = valBool f.Value = valBool
f.HasBeenSet = true } else {
// empty value implies that the env is defined but set to empty string, we have to assume that this is
// what the user wants. If user doesnt want this then the env needs to be deleted or the flag removed from
// file
f.Value = false
} }
f.HasBeenSet = true
}
count := f.Count
dest := f.Destination
if count == nil {
count = new(int)
}
if dest == nil {
dest = new(bool)
} }
for _, name := range f.Names() { for _, name := range f.Names() {
if f.Destination != nil { value := newBoolValue(f.Value, dest, count)
set.BoolVar(f.Destination, name, f.Value, f.Usage) set.Var(value, name, f.Usage)
continue
}
set.Bool(name, f.Value, f.Usage)
} }
return nil return nil

View File

@ -32,7 +32,7 @@ func (f *DurationFlag) GetDefaultText() string {
if f.DefaultText != "" { if f.DefaultText != "" {
return f.DefaultText return f.DefaultText
} }
return f.GetValue() return f.defaultValue.String()
} }
// GetEnvVars returns the env vars for this flag // GetEnvVars returns the env vars for this flag
@ -42,6 +42,9 @@ func (f *DurationFlag) GetEnvVars() []string {
// Apply populates the flag given the flag set and environment // Apply populates the flag given the flag set and environment
func (f *DurationFlag) Apply(set *flag.FlagSet) error { func (f *DurationFlag) Apply(set *flag.FlagSet) error {
// set default value so that environment wont be able to overwrite it
f.defaultValue = f.Value
if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found { if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {
if val != "" { if val != "" {
valDuration, err := time.ParseDuration(val) valDuration, err := time.ParseDuration(val)
@ -70,6 +73,15 @@ func (f *DurationFlag) Get(ctx *Context) time.Duration {
return ctx.Duration(f.Name) return ctx.Duration(f.Name)
} }
// RunAction executes flag action if set
func (f *DurationFlag) RunAction(c *Context) error {
if f.Action != nil {
return f.Action(c, c.Duration(f.Name))
}
return nil
}
// Duration looks up the value of a local DurationFlag, returns // Duration looks up the value of a local DurationFlag, returns
// 0 if not found // 0 if not found
func (cCtx *Context) Duration(name string) time.Duration { func (cCtx *Context) Duration(name string) time.Duration {

48
vendor/github.com/urfave/cli/v2/flag_ext.go generated vendored Normal file
View File

@ -0,0 +1,48 @@
package cli
import "flag"
type extFlag struct {
f *flag.Flag
}
func (e *extFlag) Apply(fs *flag.FlagSet) error {
fs.Var(e.f.Value, e.f.Name, e.f.Usage)
return nil
}
func (e *extFlag) Names() []string {
return []string{e.f.Name}
}
func (e *extFlag) IsSet() bool {
return false
}
func (e *extFlag) String() string {
return FlagStringer(e)
}
func (e *extFlag) IsVisible() bool {
return true
}
func (e *extFlag) TakesValue() bool {
return false
}
func (e *extFlag) GetUsage() string {
return e.f.Usage
}
func (e *extFlag) GetValue() string {
return e.f.Value.String()
}
func (e *extFlag) GetDefaultText() string {
return e.f.DefValue
}
func (e *extFlag) GetEnvVars() []string {
return nil
}

View File

@ -70,6 +70,15 @@ func (f *Float64Flag) Get(ctx *Context) float64 {
return ctx.Float64(f.Name) return ctx.Float64(f.Name)
} }
// RunAction executes flag action if set
func (f *Float64Flag) RunAction(c *Context) error {
if f.Action != nil {
return f.Action(c, c.Float64(f.Name))
}
return nil
}
// Float64 looks up the value of a local Float64Flag, returns // Float64 looks up the value of a local Float64Flag, returns
// 0 if not found // 0 if not found
func (cCtx *Context) Float64(name string) float64 { func (cCtx *Context) Float64(name string) float64 {

View File

@ -11,6 +11,7 @@ import (
// Float64Slice wraps []float64 to satisfy flag.Value // Float64Slice wraps []float64 to satisfy flag.Value
type Float64Slice struct { type Float64Slice struct {
slice []float64 slice []float64
separator separatorSpec
hasBeenSet bool hasBeenSet bool
} }
@ -29,6 +30,10 @@ func (f *Float64Slice) clone() *Float64Slice {
return n return n
} }
func (f *Float64Slice) WithSeparatorSpec(spec separatorSpec) {
f.separator = spec
}
// Set parses the value into a float64 and appends it to the list of values // Set parses the value into a float64 and appends it to the list of values
func (f *Float64Slice) Set(value string) error { func (f *Float64Slice) Set(value string) error {
if !f.hasBeenSet { if !f.hasBeenSet {
@ -43,7 +48,7 @@ func (f *Float64Slice) Set(value string) error {
return nil return nil
} }
for _, s := range flagSplitMultiValues(value) { for _, s := range f.separator.flagSplitMultiValues(value) {
tmp, err := strconv.ParseFloat(strings.TrimSpace(s), 64) tmp, err := strconv.ParseFloat(strings.TrimSpace(s), 64)
if err != nil { if err != nil {
return err return err
@ -83,7 +88,7 @@ func (f *Float64Slice) Get() interface{} {
// String returns a readable representation of this value // String returns a readable representation of this value
// (for usage defaults) // (for usage defaults)
func (f *Float64SliceFlag) String() string { func (f *Float64SliceFlag) String() string {
return withEnvHint(f.GetEnvVars(), stringifyFloat64SliceFlag(f)) return FlagStringer(f)
} }
// TakesValue returns true if the flag takes a value, otherwise false // TakesValue returns true if the flag takes a value, otherwise false
@ -104,10 +109,13 @@ func (f *Float64SliceFlag) GetCategory() string {
// GetValue returns the flags value as string representation and an empty // GetValue returns the flags value as string representation and an empty
// string if the flag takes no value at all. // string if the flag takes no value at all.
func (f *Float64SliceFlag) GetValue() string { func (f *Float64SliceFlag) GetValue() string {
if f.Value != nil { var defaultVals []string
return f.Value.String() if f.Value != nil && len(f.Value.Value()) > 0 {
for _, i := range f.Value.Value() {
defaultVals = append(defaultVals, strings.TrimRight(strings.TrimRight(fmt.Sprintf("%f", i), "0"), "."))
}
} }
return "" return strings.Join(defaultVals, ", ")
} }
// GetDefaultText returns the default text for this flag // GetDefaultText returns the default text for this flag
@ -123,6 +131,11 @@ func (f *Float64SliceFlag) GetEnvVars() []string {
return f.EnvVars return f.EnvVars
} }
// IsSliceFlag implements DocGenerationSliceFlag.
func (f *Float64SliceFlag) IsSliceFlag() bool {
return true
}
// Apply populates the flag given the flag set and environment // Apply populates the flag given the flag set and environment
func (f *Float64SliceFlag) Apply(set *flag.FlagSet) error { func (f *Float64SliceFlag) Apply(set *flag.FlagSet) error {
// apply any default // apply any default
@ -140,11 +153,12 @@ func (f *Float64SliceFlag) Apply(set *flag.FlagSet) error {
setValue = f.Value.clone() setValue = f.Value.clone()
default: default:
setValue = new(Float64Slice) setValue = new(Float64Slice)
setValue.WithSeparatorSpec(f.separator)
} }
if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found { if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {
if val != "" { if val != "" {
for _, s := range flagSplitMultiValues(val) { for _, s := range f.separator.flagSplitMultiValues(val) {
if err := setValue.Set(strings.TrimSpace(s)); err != nil { if err := setValue.Set(strings.TrimSpace(s)); err != nil {
return fmt.Errorf("could not parse %q as float64 slice value from %s for flag %s: %s", val, source, f.Name, err) return fmt.Errorf("could not parse %q as float64 slice value from %s for flag %s: %s", val, source, f.Name, err)
} }
@ -164,11 +178,24 @@ func (f *Float64SliceFlag) Apply(set *flag.FlagSet) error {
return nil return nil
} }
func (f *Float64SliceFlag) WithSeparatorSpec(spec separatorSpec) {
f.separator = spec
}
// Get returns the flags value in the given Context. // Get returns the flags value in the given Context.
func (f *Float64SliceFlag) Get(ctx *Context) []float64 { func (f *Float64SliceFlag) Get(ctx *Context) []float64 {
return ctx.Float64Slice(f.Name) return ctx.Float64Slice(f.Name)
} }
// RunAction executes flag action if set
func (f *Float64SliceFlag) RunAction(c *Context) error {
if f.Action != nil {
return f.Action(c, c.Float64Slice(f.Name))
}
return nil
}
// Float64Slice looks up the value of a local Float64SliceFlag, returns // Float64Slice looks up the value of a local Float64SliceFlag, returns
// nil if not found // nil if not found
func (cCtx *Context) Float64Slice(name string) []float64 { func (cCtx *Context) Float64Slice(name string) []float64 {

View File

@ -11,6 +11,19 @@ type Generic interface {
String() string String() string
} }
type stringGeneric struct {
value string
}
func (s *stringGeneric) Set(value string) error {
s.value = value
return nil
}
func (s *stringGeneric) String() string {
return s.value
}
// TakesValue returns true of the flag takes a value, otherwise false // TakesValue returns true of the flag takes a value, otherwise false
func (f *GenericFlag) TakesValue() bool { func (f *GenericFlag) TakesValue() bool {
return true return true
@ -40,7 +53,10 @@ func (f *GenericFlag) GetDefaultText() string {
if f.DefaultText != "" { if f.DefaultText != "" {
return f.DefaultText return f.DefaultText
} }
return f.GetValue() if f.defaultValue != nil {
return f.defaultValue.String()
}
return ""
} }
// GetEnvVars returns the env vars for this flag // GetEnvVars returns the env vars for this flag
@ -50,7 +66,12 @@ func (f *GenericFlag) GetEnvVars() []string {
// Apply takes the flagset and calls Set on the generic flag with the value // Apply takes the flagset and calls Set on the generic flag with the value
// provided by the user for parsing by the flag // provided by the user for parsing by the flag
func (f GenericFlag) Apply(set *flag.FlagSet) error { func (f *GenericFlag) Apply(set *flag.FlagSet) error {
// set default value so that environment wont be able to overwrite it
if f.Value != nil {
f.defaultValue = &stringGeneric{value: f.Value.String()}
}
if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found { if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {
if val != "" { if val != "" {
if err := f.Value.Set(val); err != nil { if err := f.Value.Set(val); err != nil {
@ -62,6 +83,10 @@ func (f GenericFlag) Apply(set *flag.FlagSet) error {
} }
for _, name := range f.Names() { for _, name := range f.Names() {
if f.Destination != nil {
set.Var(f.Destination, name, f.Usage)
continue
}
set.Var(f.Value, name, f.Usage) set.Var(f.Value, name, f.Usage)
} }
@ -73,6 +98,15 @@ func (f *GenericFlag) Get(ctx *Context) interface{} {
return ctx.Generic(f.Name) return ctx.Generic(f.Name)
} }
// RunAction executes flag action if set
func (f *GenericFlag) RunAction(c *Context) error {
if f.Action != nil {
return f.Action(c, c.Generic(f.Name))
}
return nil
}
// Generic looks up the value of a local GenericFlag, returns // Generic looks up the value of a local GenericFlag, returns
// nil if not found // nil if not found
func (cCtx *Context) Generic(name string) interface{} { func (cCtx *Context) Generic(name string) interface{} {

View File

@ -32,7 +32,7 @@ func (f *IntFlag) GetDefaultText() string {
if f.DefaultText != "" { if f.DefaultText != "" {
return f.DefaultText return f.DefaultText
} }
return f.GetValue() return fmt.Sprintf("%d", f.defaultValue)
} }
// GetEnvVars returns the env vars for this flag // GetEnvVars returns the env vars for this flag
@ -42,9 +42,12 @@ func (f *IntFlag) GetEnvVars() []string {
// Apply populates the flag given the flag set and environment // Apply populates the flag given the flag set and environment
func (f *IntFlag) Apply(set *flag.FlagSet) error { func (f *IntFlag) Apply(set *flag.FlagSet) error {
// set default value so that environment wont be able to overwrite it
f.defaultValue = f.Value
if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found { if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {
if val != "" { if val != "" {
valInt, err := strconv.ParseInt(val, 0, 64) valInt, err := strconv.ParseInt(val, f.Base, 64)
if err != nil { if err != nil {
return fmt.Errorf("could not parse %q as int value from %s for flag %s: %s", val, source, f.Name, err) return fmt.Errorf("could not parse %q as int value from %s for flag %s: %s", val, source, f.Name, err)
@ -71,6 +74,15 @@ func (f *IntFlag) Get(ctx *Context) int {
return ctx.Int(f.Name) return ctx.Int(f.Name)
} }
// RunAction executes flag action if set
func (f *IntFlag) RunAction(c *Context) error {
if f.Action != nil {
return f.Action(c, c.Int(f.Name))
}
return nil
}
// Int looks up the value of a local IntFlag, returns // Int looks up the value of a local IntFlag, returns
// 0 if not found // 0 if not found
func (cCtx *Context) Int(name string) int { func (cCtx *Context) Int(name string) int {

View File

@ -32,7 +32,7 @@ func (f *Int64Flag) GetDefaultText() string {
if f.DefaultText != "" { if f.DefaultText != "" {
return f.DefaultText return f.DefaultText
} }
return f.GetValue() return fmt.Sprintf("%d", f.defaultValue)
} }
// GetEnvVars returns the env vars for this flag // GetEnvVars returns the env vars for this flag
@ -42,9 +42,12 @@ func (f *Int64Flag) GetEnvVars() []string {
// Apply populates the flag given the flag set and environment // Apply populates the flag given the flag set and environment
func (f *Int64Flag) Apply(set *flag.FlagSet) error { func (f *Int64Flag) Apply(set *flag.FlagSet) error {
// set default value so that environment wont be able to overwrite it
f.defaultValue = f.Value
if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found { if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {
if val != "" { if val != "" {
valInt, err := strconv.ParseInt(val, 0, 64) valInt, err := strconv.ParseInt(val, f.Base, 64)
if err != nil { if err != nil {
return fmt.Errorf("could not parse %q as int value from %s for flag %s: %s", val, source, f.Name, err) return fmt.Errorf("could not parse %q as int value from %s for flag %s: %s", val, source, f.Name, err)
@ -70,6 +73,15 @@ func (f *Int64Flag) Get(ctx *Context) int64 {
return ctx.Int64(f.Name) return ctx.Int64(f.Name)
} }
// RunAction executes flag action if set
func (f *Int64Flag) RunAction(c *Context) error {
if f.Action != nil {
return f.Action(c, c.Int64(f.Name))
}
return nil
}
// Int64 looks up the value of a local Int64Flag, returns // Int64 looks up the value of a local Int64Flag, returns
// 0 if not found // 0 if not found
func (cCtx *Context) Int64(name string) int64 { func (cCtx *Context) Int64(name string) int64 {

View File

@ -11,6 +11,7 @@ import (
// Int64Slice wraps []int64 to satisfy flag.Value // Int64Slice wraps []int64 to satisfy flag.Value
type Int64Slice struct { type Int64Slice struct {
slice []int64 slice []int64
separator separatorSpec
hasBeenSet bool hasBeenSet bool
} }
@ -29,6 +30,10 @@ func (i *Int64Slice) clone() *Int64Slice {
return n return n
} }
func (i *Int64Slice) WithSeparatorSpec(spec separatorSpec) {
i.separator = spec
}
// Set parses the value into an integer and appends it to the list of values // Set parses the value into an integer and appends it to the list of values
func (i *Int64Slice) Set(value string) error { func (i *Int64Slice) Set(value string) error {
if !i.hasBeenSet { if !i.hasBeenSet {
@ -43,7 +48,7 @@ func (i *Int64Slice) Set(value string) error {
return nil return nil
} }
for _, s := range flagSplitMultiValues(value) { for _, s := range i.separator.flagSplitMultiValues(value) {
tmp, err := strconv.ParseInt(strings.TrimSpace(s), 0, 64) tmp, err := strconv.ParseInt(strings.TrimSpace(s), 0, 64)
if err != nil { if err != nil {
return err return err
@ -84,7 +89,7 @@ func (i *Int64Slice) Get() interface{} {
// String returns a readable representation of this value // String returns a readable representation of this value
// (for usage defaults) // (for usage defaults)
func (f *Int64SliceFlag) String() string { func (f *Int64SliceFlag) String() string {
return withEnvHint(f.GetEnvVars(), stringifyInt64SliceFlag(f)) return FlagStringer(f)
} }
// TakesValue returns true of the flag takes a value, otherwise false // TakesValue returns true of the flag takes a value, otherwise false
@ -105,10 +110,13 @@ func (f *Int64SliceFlag) GetCategory() string {
// GetValue returns the flags value as string representation and an empty // GetValue returns the flags value as string representation and an empty
// string if the flag takes no value at all. // string if the flag takes no value at all.
func (f *Int64SliceFlag) GetValue() string { func (f *Int64SliceFlag) GetValue() string {
if f.Value != nil { var defaultVals []string
return f.Value.String() if f.Value != nil && len(f.Value.Value()) > 0 {
for _, i := range f.Value.Value() {
defaultVals = append(defaultVals, strconv.FormatInt(i, 10))
}
} }
return "" return strings.Join(defaultVals, ", ")
} }
// GetDefaultText returns the default text for this flag // GetDefaultText returns the default text for this flag
@ -124,6 +132,11 @@ func (f *Int64SliceFlag) GetEnvVars() []string {
return f.EnvVars return f.EnvVars
} }
// IsSliceFlag implements DocGenerationSliceFlag.
func (f *Int64SliceFlag) IsSliceFlag() bool {
return true
}
// Apply populates the flag given the flag set and environment // Apply populates the flag given the flag set and environment
func (f *Int64SliceFlag) Apply(set *flag.FlagSet) error { func (f *Int64SliceFlag) Apply(set *flag.FlagSet) error {
// apply any default // apply any default
@ -141,10 +154,11 @@ func (f *Int64SliceFlag) Apply(set *flag.FlagSet) error {
setValue = f.Value.clone() setValue = f.Value.clone()
default: default:
setValue = new(Int64Slice) setValue = new(Int64Slice)
setValue.WithSeparatorSpec(f.separator)
} }
if val, source, ok := flagFromEnvOrFile(f.EnvVars, f.FilePath); ok && val != "" { if val, source, ok := flagFromEnvOrFile(f.EnvVars, f.FilePath); ok && val != "" {
for _, s := range flagSplitMultiValues(val) { for _, s := range f.separator.flagSplitMultiValues(val) {
if err := setValue.Set(strings.TrimSpace(s)); err != nil { if err := setValue.Set(strings.TrimSpace(s)); err != nil {
return fmt.Errorf("could not parse %q as int64 slice value from %s for flag %s: %s", val, source, f.Name, err) return fmt.Errorf("could not parse %q as int64 slice value from %s for flag %s: %s", val, source, f.Name, err)
} }
@ -163,11 +177,24 @@ func (f *Int64SliceFlag) Apply(set *flag.FlagSet) error {
return nil return nil
} }
func (f *Int64SliceFlag) WithSeparatorSpec(spec separatorSpec) {
f.separator = spec
}
// Get returns the flags value in the given Context. // Get returns the flags value in the given Context.
func (f *Int64SliceFlag) Get(ctx *Context) []int64 { func (f *Int64SliceFlag) Get(ctx *Context) []int64 {
return ctx.Int64Slice(f.Name) return ctx.Int64Slice(f.Name)
} }
// RunAction executes flag action if set
func (f *Int64SliceFlag) RunAction(c *Context) error {
if f.Action != nil {
return f.Action(c, c.Int64Slice(f.Name))
}
return nil
}
// Int64Slice looks up the value of a local Int64SliceFlag, returns // Int64Slice looks up the value of a local Int64SliceFlag, returns
// nil if not found // nil if not found
func (cCtx *Context) Int64Slice(name string) []int64 { func (cCtx *Context) Int64Slice(name string) []int64 {

View File

@ -11,6 +11,7 @@ import (
// IntSlice wraps []int to satisfy flag.Value // IntSlice wraps []int to satisfy flag.Value
type IntSlice struct { type IntSlice struct {
slice []int slice []int
separator separatorSpec
hasBeenSet bool hasBeenSet bool
} }
@ -40,6 +41,10 @@ func (i *IntSlice) SetInt(value int) {
i.slice = append(i.slice, value) i.slice = append(i.slice, value)
} }
func (i *IntSlice) WithSeparatorSpec(spec separatorSpec) {
i.separator = spec
}
// Set parses the value into an integer and appends it to the list of values // Set parses the value into an integer and appends it to the list of values
func (i *IntSlice) Set(value string) error { func (i *IntSlice) Set(value string) error {
if !i.hasBeenSet { if !i.hasBeenSet {
@ -54,7 +59,7 @@ func (i *IntSlice) Set(value string) error {
return nil return nil
} }
for _, s := range flagSplitMultiValues(value) { for _, s := range i.separator.flagSplitMultiValues(value) {
tmp, err := strconv.ParseInt(strings.TrimSpace(s), 0, 64) tmp, err := strconv.ParseInt(strings.TrimSpace(s), 0, 64)
if err != nil { if err != nil {
return err return err
@ -95,7 +100,7 @@ func (i *IntSlice) Get() interface{} {
// String returns a readable representation of this value // String returns a readable representation of this value
// (for usage defaults) // (for usage defaults)
func (f *IntSliceFlag) String() string { func (f *IntSliceFlag) String() string {
return withEnvHint(f.GetEnvVars(), stringifyIntSliceFlag(f)) return FlagStringer(f)
} }
// TakesValue returns true of the flag takes a value, otherwise false // TakesValue returns true of the flag takes a value, otherwise false
@ -116,10 +121,13 @@ func (f *IntSliceFlag) GetCategory() string {
// GetValue returns the flags value as string representation and an empty // GetValue returns the flags value as string representation and an empty
// string if the flag takes no value at all. // string if the flag takes no value at all.
func (f *IntSliceFlag) GetValue() string { func (f *IntSliceFlag) GetValue() string {
if f.Value != nil { var defaultVals []string
return f.Value.String() if f.Value != nil && len(f.Value.Value()) > 0 {
for _, i := range f.Value.Value() {
defaultVals = append(defaultVals, strconv.Itoa(i))
}
} }
return "" return strings.Join(defaultVals, ", ")
} }
// GetDefaultText returns the default text for this flag // GetDefaultText returns the default text for this flag
@ -135,6 +143,11 @@ func (f *IntSliceFlag) GetEnvVars() []string {
return f.EnvVars return f.EnvVars
} }
// IsSliceFlag implements DocGenerationSliceFlag.
func (f *IntSliceFlag) IsSliceFlag() bool {
return true
}
// Apply populates the flag given the flag set and environment // Apply populates the flag given the flag set and environment
func (f *IntSliceFlag) Apply(set *flag.FlagSet) error { func (f *IntSliceFlag) Apply(set *flag.FlagSet) error {
// apply any default // apply any default
@ -152,10 +165,11 @@ func (f *IntSliceFlag) Apply(set *flag.FlagSet) error {
setValue = f.Value.clone() setValue = f.Value.clone()
default: default:
setValue = new(IntSlice) setValue = new(IntSlice)
setValue.WithSeparatorSpec(f.separator)
} }
if val, source, ok := flagFromEnvOrFile(f.EnvVars, f.FilePath); ok && val != "" { if val, source, ok := flagFromEnvOrFile(f.EnvVars, f.FilePath); ok && val != "" {
for _, s := range flagSplitMultiValues(val) { for _, s := range f.separator.flagSplitMultiValues(val) {
if err := setValue.Set(strings.TrimSpace(s)); err != nil { if err := setValue.Set(strings.TrimSpace(s)); err != nil {
return fmt.Errorf("could not parse %q as int slice value from %s for flag %s: %s", val, source, f.Name, err) return fmt.Errorf("could not parse %q as int slice value from %s for flag %s: %s", val, source, f.Name, err)
} }
@ -174,11 +188,24 @@ func (f *IntSliceFlag) Apply(set *flag.FlagSet) error {
return nil return nil
} }
func (f *IntSliceFlag) WithSeparatorSpec(spec separatorSpec) {
f.separator = spec
}
// Get returns the flags value in the given Context. // Get returns the flags value in the given Context.
func (f *IntSliceFlag) Get(ctx *Context) []int { func (f *IntSliceFlag) Get(ctx *Context) []int {
return ctx.IntSlice(f.Name) return ctx.IntSlice(f.Name)
} }
// RunAction executes flag action if set
func (f *IntSliceFlag) RunAction(c *Context) error {
if f.Action != nil {
return f.Action(c, c.IntSlice(f.Name))
}
return nil
}
// IntSlice looks up the value of a local IntSliceFlag, returns // IntSlice looks up the value of a local IntSliceFlag, returns
// nil if not found // nil if not found
func (cCtx *Context) IntSlice(name string) []int { func (cCtx *Context) IntSlice(name string) []int {

View File

@ -33,10 +33,10 @@ func (f *PathFlag) GetDefaultText() string {
if f.DefaultText != "" { if f.DefaultText != "" {
return f.DefaultText return f.DefaultText
} }
if f.Value == "" { if f.defaultValue == "" {
return f.Value return f.defaultValue
} }
return fmt.Sprintf("%q", f.Value) return fmt.Sprintf("%q", f.defaultValue)
} }
// GetEnvVars returns the env vars for this flag // GetEnvVars returns the env vars for this flag
@ -46,6 +46,9 @@ func (f *PathFlag) GetEnvVars() []string {
// Apply populates the flag given the flag set and environment // Apply populates the flag given the flag set and environment
func (f *PathFlag) Apply(set *flag.FlagSet) error { func (f *PathFlag) Apply(set *flag.FlagSet) error {
// set default value so that environment wont be able to overwrite it
f.defaultValue = f.Value
if val, _, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found { if val, _, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {
f.Value = val f.Value = val
f.HasBeenSet = true f.HasBeenSet = true
@ -67,6 +70,15 @@ func (f *PathFlag) Get(ctx *Context) string {
return ctx.Path(f.Name) return ctx.Path(f.Name)
} }
// RunAction executes flag action if set
func (f *PathFlag) RunAction(c *Context) error {
if f.Action != nil {
return f.Action(c, c.Path(f.Name))
}
return nil
}
// Path looks up the value of a local PathFlag, returns // Path looks up the value of a local PathFlag, returns
// "" if not found // "" if not found
func (cCtx *Context) Path(name string) string { func (cCtx *Context) Path(name string) string {

View File

@ -31,10 +31,10 @@ func (f *StringFlag) GetDefaultText() string {
if f.DefaultText != "" { if f.DefaultText != "" {
return f.DefaultText return f.DefaultText
} }
if f.Value == "" { if f.defaultValue == "" {
return f.Value return f.defaultValue
} }
return fmt.Sprintf("%q", f.Value) return fmt.Sprintf("%q", f.defaultValue)
} }
// GetEnvVars returns the env vars for this flag // GetEnvVars returns the env vars for this flag
@ -44,6 +44,9 @@ func (f *StringFlag) GetEnvVars() []string {
// Apply populates the flag given the flag set and environment // Apply populates the flag given the flag set and environment
func (f *StringFlag) Apply(set *flag.FlagSet) error { func (f *StringFlag) Apply(set *flag.FlagSet) error {
// set default value so that environment wont be able to overwrite it
f.defaultValue = f.Value
if val, _, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found { if val, _, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {
f.Value = val f.Value = val
f.HasBeenSet = true f.HasBeenSet = true
@ -65,6 +68,15 @@ func (f *StringFlag) Get(ctx *Context) string {
return ctx.String(f.Name) return ctx.String(f.Name)
} }
// RunAction executes flag action if set
func (f *StringFlag) RunAction(c *Context) error {
if f.Action != nil {
return f.Action(c, c.String(f.Name))
}
return nil
}
// String looks up the value of a local StringFlag, returns // String looks up the value of a local StringFlag, returns
// "" if not found // "" if not found
func (cCtx *Context) String(name string) string { func (cCtx *Context) String(name string) string {

View File

@ -4,13 +4,16 @@ import (
"encoding/json" "encoding/json"
"flag" "flag"
"fmt" "fmt"
"strconv"
"strings" "strings"
) )
// StringSlice wraps a []string to satisfy flag.Value // StringSlice wraps a []string to satisfy flag.Value
type StringSlice struct { type StringSlice struct {
slice []string slice []string
separator separatorSpec
hasBeenSet bool hasBeenSet bool
keepSpace bool
} }
// NewStringSlice creates a *StringSlice with default values // NewStringSlice creates a *StringSlice with default values
@ -42,13 +45,20 @@ func (s *StringSlice) Set(value string) error {
return nil return nil
} }
for _, t := range flagSplitMultiValues(value) { for _, t := range s.separator.flagSplitMultiValues(value) {
s.slice = append(s.slice, strings.TrimSpace(t)) if !s.keepSpace {
t = strings.TrimSpace(t)
}
s.slice = append(s.slice, t)
} }
return nil return nil
} }
func (s *StringSlice) WithSeparatorSpec(spec separatorSpec) {
s.separator = spec
}
// String returns a readable representation of this value (for usage defaults) // String returns a readable representation of this value (for usage defaults)
func (s *StringSlice) String() string { func (s *StringSlice) String() string {
return fmt.Sprintf("%s", s.slice) return fmt.Sprintf("%s", s.slice)
@ -73,7 +83,7 @@ func (s *StringSlice) Get() interface{} {
// String returns a readable representation of this value // String returns a readable representation of this value
// (for usage defaults) // (for usage defaults)
func (f *StringSliceFlag) String() string { func (f *StringSliceFlag) String() string {
return withEnvHint(f.GetEnvVars(), stringifyStringSliceFlag(f)) return FlagStringer(f)
} }
// TakesValue returns true of the flag takes a value, otherwise false // TakesValue returns true of the flag takes a value, otherwise false
@ -94,10 +104,15 @@ func (f *StringSliceFlag) GetCategory() string {
// GetValue returns the flags value as string representation and an empty // GetValue returns the flags value as string representation and an empty
// string if the flag takes no value at all. // string if the flag takes no value at all.
func (f *StringSliceFlag) GetValue() string { func (f *StringSliceFlag) GetValue() string {
if f.Value != nil { var defaultVals []string
return f.Value.String() if f.Value != nil && len(f.Value.Value()) > 0 {
for _, s := range f.Value.Value() {
if len(s) > 0 {
defaultVals = append(defaultVals, strconv.Quote(s))
}
}
} }
return "" return strings.Join(defaultVals, ", ")
} }
// GetDefaultText returns the default text for this flag // GetDefaultText returns the default text for this flag
@ -113,6 +128,11 @@ func (f *StringSliceFlag) GetEnvVars() []string {
return f.EnvVars return f.EnvVars
} }
// IsSliceFlag implements DocGenerationSliceFlag.
func (f *StringSliceFlag) IsSliceFlag() bool {
return true
}
// Apply populates the flag given the flag set and environment // Apply populates the flag given the flag set and environment
func (f *StringSliceFlag) Apply(set *flag.FlagSet) error { func (f *StringSliceFlag) Apply(set *flag.FlagSet) error {
// apply any default // apply any default
@ -130,11 +150,17 @@ func (f *StringSliceFlag) Apply(set *flag.FlagSet) error {
setValue = f.Value.clone() setValue = f.Value.clone()
default: default:
setValue = new(StringSlice) setValue = new(StringSlice)
setValue.WithSeparatorSpec(f.separator)
} }
setValue.keepSpace = f.KeepSpace
if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found { if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {
for _, s := range flagSplitMultiValues(val) { for _, s := range f.separator.flagSplitMultiValues(val) {
if err := setValue.Set(strings.TrimSpace(s)); err != nil { if !f.KeepSpace {
s = strings.TrimSpace(s)
}
if err := setValue.Set(s); err != nil {
return fmt.Errorf("could not parse %q as string value from %s for flag %s: %s", val, source, f.Name, err) return fmt.Errorf("could not parse %q as string value from %s for flag %s: %s", val, source, f.Name, err)
} }
} }
@ -152,11 +178,24 @@ func (f *StringSliceFlag) Apply(set *flag.FlagSet) error {
return nil return nil
} }
func (f *StringSliceFlag) WithSeparatorSpec(spec separatorSpec) {
f.separator = spec
}
// Get returns the flags value in the given Context. // Get returns the flags value in the given Context.
func (f *StringSliceFlag) Get(ctx *Context) []string { func (f *StringSliceFlag) Get(ctx *Context) []string {
return ctx.StringSlice(f.Name) return ctx.StringSlice(f.Name)
} }
// RunAction executes flag action if set
func (f *StringSliceFlag) RunAction(c *Context) error {
if f.Action != nil {
return f.Action(c, c.StringSlice(f.Name))
}
return nil
}
// StringSlice looks up the value of a local StringSliceFlag, returns // StringSlice looks up the value of a local StringSliceFlag, returns
// nil if not found // nil if not found
func (cCtx *Context) StringSlice(name string) []string { func (cCtx *Context) StringSlice(name string) []string {

View File

@ -11,6 +11,7 @@ type Timestamp struct {
timestamp *time.Time timestamp *time.Time
hasBeenSet bool hasBeenSet bool
layout string layout string
location *time.Location
} }
// Timestamp constructor // Timestamp constructor
@ -31,9 +32,22 @@ func (t *Timestamp) SetLayout(layout string) {
t.layout = layout t.layout = layout
} }
// Set perceived timezone of the to-be parsed time string
func (t *Timestamp) SetLocation(loc *time.Location) {
t.location = loc
}
// Parses the string value to timestamp // Parses the string value to timestamp
func (t *Timestamp) Set(value string) error { func (t *Timestamp) Set(value string) error {
timestamp, err := time.Parse(t.layout, value) var timestamp time.Time
var err error
if t.location != nil {
timestamp, err = time.ParseInLocation(t.layout, value, t.location)
} else {
timestamp, err = time.Parse(t.layout, value)
}
if err != nil { if err != nil {
return err return err
} }
@ -58,6 +72,25 @@ func (t *Timestamp) Get() interface{} {
return *t return *t
} }
// clone timestamp
func (t *Timestamp) clone() *Timestamp {
tc := &Timestamp{
timestamp: nil,
hasBeenSet: t.hasBeenSet,
layout: t.layout,
location: nil,
}
if t.timestamp != nil {
tts := *t.timestamp
tc.timestamp = &tts
}
if t.location != nil {
loc := *t.location
tc.location = &loc
}
return tc
}
// TakesValue returns true of the flag takes a value, otherwise false // TakesValue returns true of the flag takes a value, otherwise false
func (f *TimestampFlag) TakesValue() bool { func (f *TimestampFlag) TakesValue() bool {
return true return true
@ -76,7 +109,7 @@ func (f *TimestampFlag) GetCategory() string {
// GetValue returns the flags value as string representation and an empty // GetValue returns the flags value as string representation and an empty
// string if the flag takes no value at all. // string if the flag takes no value at all.
func (f *TimestampFlag) GetValue() string { func (f *TimestampFlag) GetValue() string {
if f.Value != nil { if f.Value != nil && f.Value.timestamp != nil {
return f.Value.timestamp.String() return f.Value.timestamp.String()
} }
return "" return ""
@ -87,7 +120,11 @@ func (f *TimestampFlag) GetDefaultText() string {
if f.DefaultText != "" { if f.DefaultText != "" {
return f.DefaultText return f.DefaultText
} }
return f.GetValue() if f.defaultValue != nil && f.defaultValue.timestamp != nil {
return f.defaultValue.timestamp.String()
}
return ""
} }
// GetEnvVars returns the env vars for this flag // GetEnvVars returns the env vars for this flag
@ -104,9 +141,13 @@ func (f *TimestampFlag) Apply(set *flag.FlagSet) error {
f.Value = &Timestamp{} f.Value = &Timestamp{}
} }
f.Value.SetLayout(f.Layout) f.Value.SetLayout(f.Layout)
f.Value.SetLocation(f.Timezone)
f.defaultValue = f.Value.clone()
if f.Destination != nil { if f.Destination != nil {
f.Destination.SetLayout(f.Layout) f.Destination.SetLayout(f.Layout)
f.Destination.SetLocation(f.Timezone)
} }
if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found { if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {
@ -132,6 +173,15 @@ func (f *TimestampFlag) Get(ctx *Context) *time.Time {
return ctx.Timestamp(f.Name) return ctx.Timestamp(f.Name)
} }
// RunAction executes flag action if set
func (f *TimestampFlag) RunAction(c *Context) error {
if f.Action != nil {
return f.Action(c, c.Timestamp(f.Name))
}
return nil
}
// Timestamp gets the timestamp from a flag name // Timestamp gets the timestamp from a flag name
func (cCtx *Context) Timestamp(name string) *time.Time { func (cCtx *Context) Timestamp(name string) *time.Time {
if fs := cCtx.lookupFlagSet(name); fs != nil { if fs := cCtx.lookupFlagSet(name); fs != nil {

View File

@ -23,9 +23,12 @@ func (f *UintFlag) GetCategory() string {
// Apply populates the flag given the flag set and environment // Apply populates the flag given the flag set and environment
func (f *UintFlag) Apply(set *flag.FlagSet) error { func (f *UintFlag) Apply(set *flag.FlagSet) error {
// set default value so that environment wont be able to overwrite it
f.defaultValue = f.Value
if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found { if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {
if val != "" { if val != "" {
valInt, err := strconv.ParseUint(val, 0, 64) valInt, err := strconv.ParseUint(val, f.Base, 64)
if err != nil { if err != nil {
return fmt.Errorf("could not parse %q as uint value from %s for flag %s: %s", val, source, f.Name, err) return fmt.Errorf("could not parse %q as uint value from %s for flag %s: %s", val, source, f.Name, err)
} }
@ -46,6 +49,15 @@ func (f *UintFlag) Apply(set *flag.FlagSet) error {
return nil return nil
} }
// RunAction executes flag action if set
func (f *UintFlag) RunAction(c *Context) error {
if f.Action != nil {
return f.Action(c, c.Uint(f.Name))
}
return nil
}
// GetValue returns the flags value as string representation and an empty // GetValue returns the flags value as string representation and an empty
// string if the flag takes no value at all. // string if the flag takes no value at all.
func (f *UintFlag) GetValue() string { func (f *UintFlag) GetValue() string {
@ -57,7 +69,7 @@ func (f *UintFlag) GetDefaultText() string {
if f.DefaultText != "" { if f.DefaultText != "" {
return f.DefaultText return f.DefaultText
} }
return f.GetValue() return fmt.Sprintf("%d", f.defaultValue)
} }
// GetEnvVars returns the env vars for this flag // GetEnvVars returns the env vars for this flag

View File

@ -23,9 +23,12 @@ func (f *Uint64Flag) GetCategory() string {
// Apply populates the flag given the flag set and environment // Apply populates the flag given the flag set and environment
func (f *Uint64Flag) Apply(set *flag.FlagSet) error { func (f *Uint64Flag) Apply(set *flag.FlagSet) error {
// set default value so that environment wont be able to overwrite it
f.defaultValue = f.Value
if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found { if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {
if val != "" { if val != "" {
valInt, err := strconv.ParseUint(val, 0, 64) valInt, err := strconv.ParseUint(val, f.Base, 64)
if err != nil { if err != nil {
return fmt.Errorf("could not parse %q as uint64 value from %s for flag %s: %s", val, source, f.Name, err) return fmt.Errorf("could not parse %q as uint64 value from %s for flag %s: %s", val, source, f.Name, err)
} }
@ -46,6 +49,15 @@ func (f *Uint64Flag) Apply(set *flag.FlagSet) error {
return nil return nil
} }
// RunAction executes flag action if set
func (f *Uint64Flag) RunAction(c *Context) error {
if f.Action != nil {
return f.Action(c, c.Uint64(f.Name))
}
return nil
}
// GetValue returns the flags value as string representation and an empty // GetValue returns the flags value as string representation and an empty
// string if the flag takes no value at all. // string if the flag takes no value at all.
func (f *Uint64Flag) GetValue() string { func (f *Uint64Flag) GetValue() string {
@ -57,7 +69,7 @@ func (f *Uint64Flag) GetDefaultText() string {
if f.DefaultText != "" { if f.DefaultText != "" {
return f.DefaultText return f.DefaultText
} }
return f.GetValue() return fmt.Sprintf("%d", f.defaultValue)
} }
// GetEnvVars returns the env vars for this flag // GetEnvVars returns the env vars for this flag

210
vendor/github.com/urfave/cli/v2/flag_uint64_slice.go generated vendored Normal file
View File

@ -0,0 +1,210 @@
package cli
import (
"encoding/json"
"flag"
"fmt"
"strconv"
"strings"
)
// Uint64Slice wraps []int64 to satisfy flag.Value
type Uint64Slice struct {
slice []uint64
separator separatorSpec
hasBeenSet bool
}
// NewUint64Slice makes an *Uint64Slice with default values
func NewUint64Slice(defaults ...uint64) *Uint64Slice {
return &Uint64Slice{slice: append([]uint64{}, defaults...)}
}
// clone allocate a copy of self object
func (i *Uint64Slice) clone() *Uint64Slice {
n := &Uint64Slice{
slice: make([]uint64, len(i.slice)),
hasBeenSet: i.hasBeenSet,
}
copy(n.slice, i.slice)
return n
}
// Set parses the value into an integer and appends it to the list of values
func (i *Uint64Slice) Set(value string) error {
if !i.hasBeenSet {
i.slice = []uint64{}
i.hasBeenSet = true
}
if strings.HasPrefix(value, slPfx) {
// Deserializing assumes overwrite
_ = json.Unmarshal([]byte(strings.Replace(value, slPfx, "", 1)), &i.slice)
i.hasBeenSet = true
return nil
}
for _, s := range i.separator.flagSplitMultiValues(value) {
tmp, err := strconv.ParseUint(strings.TrimSpace(s), 0, 64)
if err != nil {
return err
}
i.slice = append(i.slice, tmp)
}
return nil
}
func (i *Uint64Slice) WithSeparatorSpec(spec separatorSpec) {
i.separator = spec
}
// String returns a readable representation of this value (for usage defaults)
func (i *Uint64Slice) String() string {
v := i.slice
if v == nil {
// treat nil the same as zero length non-nil
v = make([]uint64, 0)
}
str := fmt.Sprintf("%d", v)
str = strings.Replace(str, " ", ", ", -1)
str = strings.Replace(str, "[", "{", -1)
str = strings.Replace(str, "]", "}", -1)
return fmt.Sprintf("[]uint64%s", str)
}
// Serialize allows Uint64Slice to fulfill Serializer
func (i *Uint64Slice) Serialize() string {
jsonBytes, _ := json.Marshal(i.slice)
return fmt.Sprintf("%s%s", slPfx, string(jsonBytes))
}
// Value returns the slice of ints set by this flag
func (i *Uint64Slice) Value() []uint64 {
return i.slice
}
// Get returns the slice of ints set by this flag
func (i *Uint64Slice) Get() interface{} {
return *i
}
// String returns a readable representation of this value
// (for usage defaults)
func (f *Uint64SliceFlag) String() string {
return FlagStringer(f)
}
// TakesValue returns true of the flag takes a value, otherwise false
func (f *Uint64SliceFlag) TakesValue() bool {
return true
}
// GetUsage returns the usage string for the flag
func (f *Uint64SliceFlag) GetUsage() string {
return f.Usage
}
// GetCategory returns the category for the flag
func (f *Uint64SliceFlag) GetCategory() string {
return f.Category
}
// GetValue returns the flags value as string representation and an empty
// string if the flag takes no value at all.
func (f *Uint64SliceFlag) GetValue() string {
var defaultVals []string
if f.Value != nil && len(f.Value.Value()) > 0 {
for _, i := range f.Value.Value() {
defaultVals = append(defaultVals, strconv.FormatUint(i, 10))
}
}
return strings.Join(defaultVals, ", ")
}
// GetDefaultText returns the default text for this flag
func (f *Uint64SliceFlag) GetDefaultText() string {
if f.DefaultText != "" {
return f.DefaultText
}
return f.GetValue()
}
// GetEnvVars returns the env vars for this flag
func (f *Uint64SliceFlag) GetEnvVars() []string {
return f.EnvVars
}
// IsSliceFlag implements DocGenerationSliceFlag.
func (f *Uint64SliceFlag) IsSliceFlag() bool {
return true
}
// Apply populates the flag given the flag set and environment
func (f *Uint64SliceFlag) Apply(set *flag.FlagSet) error {
// apply any default
if f.Destination != nil && f.Value != nil {
f.Destination.slice = make([]uint64, len(f.Value.slice))
copy(f.Destination.slice, f.Value.slice)
}
// resolve setValue (what we will assign to the set)
var setValue *Uint64Slice
switch {
case f.Destination != nil:
setValue = f.Destination
case f.Value != nil:
setValue = f.Value.clone()
default:
setValue = new(Uint64Slice)
setValue.WithSeparatorSpec(f.separator)
}
if val, source, ok := flagFromEnvOrFile(f.EnvVars, f.FilePath); ok && val != "" {
for _, s := range f.separator.flagSplitMultiValues(val) {
if err := setValue.Set(strings.TrimSpace(s)); err != nil {
return fmt.Errorf("could not parse %q as uint64 slice value from %s for flag %s: %s", val, source, f.Name, err)
}
}
// Set this to false so that we reset the slice if we then set values from
// flags that have already been set by the environment.
setValue.hasBeenSet = false
f.HasBeenSet = true
}
for _, name := range f.Names() {
set.Var(setValue, name, f.Usage)
}
return nil
}
func (f *Uint64SliceFlag) WithSeparatorSpec(spec separatorSpec) {
f.separator = spec
}
// Get returns the flags value in the given Context.
func (f *Uint64SliceFlag) Get(ctx *Context) []uint64 {
return ctx.Uint64Slice(f.Name)
}
// Uint64Slice looks up the value of a local Uint64SliceFlag, returns
// nil if not found
func (cCtx *Context) Uint64Slice(name string) []uint64 {
if fs := cCtx.lookupFlagSet(name); fs != nil {
return lookupUint64Slice(name, fs)
}
return nil
}
func lookupUint64Slice(name string, set *flag.FlagSet) []uint64 {
f := set.Lookup(name)
if f != nil {
if slice, ok := unwrapFlagValue(f.Value).(*Uint64Slice); ok {
return slice.Value()
}
}
return nil
}

221
vendor/github.com/urfave/cli/v2/flag_uint_slice.go generated vendored Normal file
View File

@ -0,0 +1,221 @@
package cli
import (
"encoding/json"
"flag"
"fmt"
"strconv"
"strings"
)
// UintSlice wraps []int to satisfy flag.Value
type UintSlice struct {
slice []uint
separator separatorSpec
hasBeenSet bool
}
// NewUintSlice makes an *UintSlice with default values
func NewUintSlice(defaults ...uint) *UintSlice {
return &UintSlice{slice: append([]uint{}, defaults...)}
}
// clone allocate a copy of self object
func (i *UintSlice) clone() *UintSlice {
n := &UintSlice{
slice: make([]uint, len(i.slice)),
hasBeenSet: i.hasBeenSet,
}
copy(n.slice, i.slice)
return n
}
// TODO: Consistently have specific Set function for Int64 and Float64 ?
// SetInt directly adds an integer to the list of values
func (i *UintSlice) SetUint(value uint) {
if !i.hasBeenSet {
i.slice = []uint{}
i.hasBeenSet = true
}
i.slice = append(i.slice, value)
}
// Set parses the value into an integer and appends it to the list of values
func (i *UintSlice) Set(value string) error {
if !i.hasBeenSet {
i.slice = []uint{}
i.hasBeenSet = true
}
if strings.HasPrefix(value, slPfx) {
// Deserializing assumes overwrite
_ = json.Unmarshal([]byte(strings.Replace(value, slPfx, "", 1)), &i.slice)
i.hasBeenSet = true
return nil
}
for _, s := range i.separator.flagSplitMultiValues(value) {
tmp, err := strconv.ParseUint(strings.TrimSpace(s), 0, 32)
if err != nil {
return err
}
i.slice = append(i.slice, uint(tmp))
}
return nil
}
func (i *UintSlice) WithSeparatorSpec(spec separatorSpec) {
i.separator = spec
}
// String returns a readable representation of this value (for usage defaults)
func (i *UintSlice) String() string {
v := i.slice
if v == nil {
// treat nil the same as zero length non-nil
v = make([]uint, 0)
}
str := fmt.Sprintf("%d", v)
str = strings.Replace(str, " ", ", ", -1)
str = strings.Replace(str, "[", "{", -1)
str = strings.Replace(str, "]", "}", -1)
return fmt.Sprintf("[]uint%s", str)
}
// Serialize allows UintSlice to fulfill Serializer
func (i *UintSlice) Serialize() string {
jsonBytes, _ := json.Marshal(i.slice)
return fmt.Sprintf("%s%s", slPfx, string(jsonBytes))
}
// Value returns the slice of ints set by this flag
func (i *UintSlice) Value() []uint {
return i.slice
}
// Get returns the slice of ints set by this flag
func (i *UintSlice) Get() interface{} {
return *i
}
// String returns a readable representation of this value
// (for usage defaults)
func (f *UintSliceFlag) String() string {
return FlagStringer(f)
}
// TakesValue returns true of the flag takes a value, otherwise false
func (f *UintSliceFlag) TakesValue() bool {
return true
}
// GetUsage returns the usage string for the flag
func (f *UintSliceFlag) GetUsage() string {
return f.Usage
}
// GetCategory returns the category for the flag
func (f *UintSliceFlag) GetCategory() string {
return f.Category
}
// GetValue returns the flags value as string representation and an empty
// string if the flag takes no value at all.
func (f *UintSliceFlag) GetValue() string {
var defaultVals []string
if f.Value != nil && len(f.Value.Value()) > 0 {
for _, i := range f.Value.Value() {
defaultVals = append(defaultVals, strconv.FormatUint(uint64(i), 10))
}
}
return strings.Join(defaultVals, ", ")
}
// GetDefaultText returns the default text for this flag
func (f *UintSliceFlag) GetDefaultText() string {
if f.DefaultText != "" {
return f.DefaultText
}
return f.GetValue()
}
// GetEnvVars returns the env vars for this flag
func (f *UintSliceFlag) GetEnvVars() []string {
return f.EnvVars
}
// IsSliceFlag implements DocGenerationSliceFlag.
func (f *UintSliceFlag) IsSliceFlag() bool {
return true
}
// Apply populates the flag given the flag set and environment
func (f *UintSliceFlag) Apply(set *flag.FlagSet) error {
// apply any default
if f.Destination != nil && f.Value != nil {
f.Destination.slice = make([]uint, len(f.Value.slice))
copy(f.Destination.slice, f.Value.slice)
}
// resolve setValue (what we will assign to the set)
var setValue *UintSlice
switch {
case f.Destination != nil:
setValue = f.Destination
case f.Value != nil:
setValue = f.Value.clone()
default:
setValue = new(UintSlice)
setValue.WithSeparatorSpec(f.separator)
}
if val, source, ok := flagFromEnvOrFile(f.EnvVars, f.FilePath); ok && val != "" {
for _, s := range f.separator.flagSplitMultiValues(val) {
if err := setValue.Set(strings.TrimSpace(s)); err != nil {
return fmt.Errorf("could not parse %q as uint slice value from %s for flag %s: %s", val, source, f.Name, err)
}
}
// Set this to false so that we reset the slice if we then set values from
// flags that have already been set by the environment.
setValue.hasBeenSet = false
f.HasBeenSet = true
}
for _, name := range f.Names() {
set.Var(setValue, name, f.Usage)
}
return nil
}
func (f *UintSliceFlag) WithSeparatorSpec(spec separatorSpec) {
f.separator = spec
}
// Get returns the flags value in the given Context.
func (f *UintSliceFlag) Get(ctx *Context) []uint {
return ctx.UintSlice(f.Name)
}
// UintSlice looks up the value of a local UintSliceFlag, returns
// nil if not found
func (cCtx *Context) UintSlice(name string) []uint {
if fs := cCtx.lookupFlagSet(name); fs != nil {
return lookupUintSlice(name, fs)
}
return nil
}
func lookupUintSlice(name string, set *flag.FlagSet) []uint {
f := set.Lookup(name)
if f != nil {
if slice, ok := unwrapFlagValue(f.Value).(*UintSlice); ok {
return slice.Value()
}
}
return nil
}

View File

@ -23,6 +23,9 @@ type CommandNotFoundFunc func(*Context, string)
// is displayed and the execution is interrupted. // is displayed and the execution is interrupted.
type OnUsageErrorFunc func(cCtx *Context, err error, isSubcommand bool) error type OnUsageErrorFunc func(cCtx *Context, err error, isSubcommand bool) error
// InvalidFlagAccessFunc is executed when an invalid flag is accessed from the context.
type InvalidFlagAccessFunc func(*Context, string)
// ExitErrHandlerFunc is executed if provided in order to handle exitError values // ExitErrHandlerFunc is executed if provided in order to handle exitError values
// returned by Actions and Before/After functions. // returned by Actions and Before/After functions.
type ExitErrHandlerFunc func(cCtx *Context, err error) type ExitErrHandlerFunc func(cCtx *Context, err error)

File diff suppressed because it is too large Load Diff

View File

@ -15,6 +15,15 @@ const (
helpAlias = "h" helpAlias = "h"
) )
// this instance is to avoid recursion in the ShowCommandHelp which can
// add a help command again
var helpCommandDontUse = &Command{
Name: helpName,
Aliases: []string{helpAlias},
Usage: "Shows a list of commands or help for one command",
ArgsUsage: "[command]",
}
var helpCommand = &Command{ var helpCommand = &Command{
Name: helpName, Name: helpName,
Aliases: []string{helpAlias}, Aliases: []string{helpAlias},
@ -22,26 +31,52 @@ var helpCommand = &Command{
ArgsUsage: "[command]", ArgsUsage: "[command]",
Action: func(cCtx *Context) error { Action: func(cCtx *Context) error {
args := cCtx.Args() args := cCtx.Args()
if args.Present() { argsPresent := args.First() != ""
return ShowCommandHelp(cCtx, args.First()) firstArg := args.First()
// This action can be triggered by a "default" action of a command
// or via cmd.Run when cmd == helpCmd. So we have following possibilities
//
// 1 $ app
// 2 $ app help
// 3 $ app foo
// 4 $ app help foo
// 5 $ app foo help
// Case 4. when executing a help command set the context to parent
// to allow resolution of subsequent args. This will transform
// $ app help foo
// to
// $ app foo
// which will then be handled as case 3
if cCtx.Command.Name == helpName || cCtx.Command.Name == helpAlias {
cCtx = cCtx.parentContext
} }
_ = ShowAppHelp(cCtx) // Case 4. $ app hello foo
return nil // foo is the command for which help needs to be shown
}, if argsPresent {
} return ShowCommandHelp(cCtx, firstArg)
var helpSubcommand = &Command{
Name: helpName,
Aliases: []string{helpAlias},
Usage: "Shows a list of commands or help for one command",
ArgsUsage: "[command]",
Action: func(cCtx *Context) error {
args := cCtx.Args()
if args.Present() {
return ShowCommandHelp(cCtx, args.First())
} }
// Case 1 & 2
// Special case when running help on main app itself as opposed to individual
// commands/subcommands
if cCtx.parentContext.App == nil {
_ = ShowAppHelp(cCtx)
return nil
}
// Case 3, 5
if (len(cCtx.Command.Subcommands) == 1 && !cCtx.Command.HideHelp) ||
(len(cCtx.Command.Subcommands) == 0 && cCtx.Command.HideHelp) {
templ := cCtx.Command.CustomHelpTemplate
if templ == "" {
templ = CommandHelpTemplate
}
HelpPrinter(cCtx.App.Writer, templ, cCtx.Command)
return nil
}
return ShowSubcommandHelp(cCtx) return ShowSubcommandHelp(cCtx)
}, },
} }
@ -153,7 +188,7 @@ func printFlagSuggestions(lastArg string, flags []Flag, writer io.Writer) {
// this will get total count utf8 letters in flag name // this will get total count utf8 letters in flag name
count := utf8.RuneCountInString(name) count := utf8.RuneCountInString(name)
if count > 2 { if count > 2 {
count = 2 // resuse this count to generate single - or -- in flag completion count = 2 // reuse this count to generate single - or -- in flag completion
} }
// if flag name has more than one utf8 letter and last argument in cli has -- prefix then // if flag name has more than one utf8 letter and last argument in cli has -- prefix then
// skip flag completion for short flags example -v or -x // skip flag completion for short flags example -v or -x
@ -192,7 +227,7 @@ func DefaultCompleteWithFlags(cmd *Command) func(cCtx *Context) {
return return
} }
printCommandSuggestions(cCtx.App.Commands, cCtx.App.Writer) printCommandSuggestions(cCtx.Command.Subcommands, cCtx.App.Writer)
} }
} }
@ -204,17 +239,26 @@ func ShowCommandHelpAndExit(c *Context, command string, code int) {
// ShowCommandHelp prints help for the given command // ShowCommandHelp prints help for the given command
func ShowCommandHelp(ctx *Context, command string) error { 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 { commands := ctx.App.Commands
if ctx.Command.Subcommands != nil {
commands = ctx.Command.Subcommands
}
for _, c := range commands {
if c.HasName(command) { if c.HasName(command) {
if !ctx.App.HideHelpCommand && !c.HasName(helpName) && len(c.Subcommands) != 0 && c.Command(helpName) == nil {
c.Subcommands = append(c.Subcommands, helpCommandDontUse)
}
if !ctx.App.HideHelp && HelpFlag != nil {
c.appendFlag(HelpFlag)
}
templ := c.CustomHelpTemplate templ := c.CustomHelpTemplate
if templ == "" { if templ == "" {
templ = CommandHelpTemplate if len(c.Subcommands) == 0 {
templ = CommandHelpTemplate
} else {
templ = SubcommandHelpTemplate
}
} }
HelpPrinter(ctx.App.Writer, templ, c) HelpPrinter(ctx.App.Writer, templ, c)
@ -226,7 +270,7 @@ func ShowCommandHelp(ctx *Context, command string) error {
if ctx.App.CommandNotFound == nil { if ctx.App.CommandNotFound == nil {
errMsg := fmt.Sprintf("No help topic for '%v'", command) errMsg := fmt.Sprintf("No help topic for '%v'", command)
if ctx.App.Suggest { if ctx.App.Suggest {
if suggestion := SuggestCommand(ctx.App.Commands, command); suggestion != "" { if suggestion := SuggestCommand(ctx.Command.Subcommands, command); suggestion != "" {
errMsg += ". " + suggestion errMsg += ". " + suggestion
} }
} }
@ -249,11 +293,8 @@ func ShowSubcommandHelp(cCtx *Context) error {
return nil return nil
} }
if cCtx.Command != nil { HelpPrinter(cCtx.App.Writer, SubcommandHelpTemplate, cCtx.Command)
return ShowCommandHelp(cCtx, cCtx.Command.Name) return nil
}
return ShowCommandHelp(cCtx, "")
} }
// ShowVersion prints the version number of the App // ShowVersion prints the version number of the App
@ -267,15 +308,15 @@ func printVersion(cCtx *Context) {
// ShowCompletions prints the lists of commands within a given context // ShowCompletions prints the lists of commands within a given context
func ShowCompletions(cCtx *Context) { func ShowCompletions(cCtx *Context) {
a := cCtx.App c := cCtx.Command
if a != nil && a.BashComplete != nil { if c != nil && c.BashComplete != nil {
a.BashComplete(cCtx) c.BashComplete(cCtx)
} }
} }
// ShowCommandCompletions prints the custom completions for a given command // ShowCommandCompletions prints the custom completions for a given command
func ShowCommandCompletions(ctx *Context, command string) { func ShowCommandCompletions(ctx *Context, command string) {
c := ctx.App.Command(command) c := ctx.Command.Command(command)
if c != nil { if c != nil {
if c.BashComplete != nil { if c.BashComplete != nil {
c.BashComplete(ctx) c.BashComplete(ctx)
@ -295,12 +336,14 @@ func printHelpCustom(out io.Writer, templ string, data interface{}, customFuncs
const maxLineLength = 10000 const maxLineLength = 10000
funcMap := template.FuncMap{ funcMap := template.FuncMap{
"join": strings.Join, "join": strings.Join,
"indent": indent, "subtract": subtract,
"nindent": nindent, "indent": indent,
"trim": strings.TrimSpace, "nindent": nindent,
"wrap": func(input string, offset int) string { return wrap(input, offset, maxLineLength) }, "trim": strings.TrimSpace,
"offset": offset, "wrap": func(input string, offset int) string { return wrap(input, offset, maxLineLength) },
"offset": offset,
"offsetCommands": offsetCommands,
} }
if customFuncs["wrapAt"] != nil { if customFuncs["wrapAt"] != nil {
@ -320,6 +363,17 @@ func printHelpCustom(out io.Writer, templ string, data interface{}, customFuncs
w := tabwriter.NewWriter(out, 1, 8, 2, ' ', 0) w := tabwriter.NewWriter(out, 1, 8, 2, ' ', 0)
t := template.Must(template.New("help").Funcs(funcMap).Parse(templ)) t := template.Must(template.New("help").Funcs(funcMap).Parse(templ))
t.New("helpNameTemplate").Parse(helpNameTemplate)
t.New("usageTemplate").Parse(usageTemplate)
t.New("descriptionTemplate").Parse(descriptionTemplate)
t.New("visibleCommandTemplate").Parse(visibleCommandTemplate)
t.New("copyrightTemplate").Parse(copyrightTemplate)
t.New("versionTemplate").Parse(versionTemplate)
t.New("visibleFlagCategoryTemplate").Parse(visibleFlagCategoryTemplate)
t.New("visibleFlagTemplate").Parse(visibleFlagTemplate)
t.New("visibleGlobalFlagCategoryTemplate").Parse(strings.Replace(visibleFlagCategoryTemplate, "OPTIONS", "GLOBAL OPTIONS", -1))
t.New("authorsTemplate").Parse(authorsTemplate)
t.New("visibleCommandCategoryTemplate").Parse(visibleCommandCategoryTemplate)
err := t.Execute(w, data) err := t.Execute(w, data)
if err != nil { if err != nil {
@ -352,8 +406,10 @@ func checkHelp(cCtx *Context) bool {
for _, name := range HelpFlag.Names() { for _, name := range HelpFlag.Names() {
if cCtx.Bool(name) { if cCtx.Bool(name) {
found = true found = true
break
} }
} }
return found return found
} }
@ -397,7 +453,7 @@ func checkCompletions(cCtx *Context) bool {
if args := cCtx.Args(); args.Present() { if args := cCtx.Args(); args.Present() {
name := args.First() name := args.First()
if cmd := cCtx.App.Command(name); cmd != nil { if cmd := cCtx.Command.Command(name); cmd != nil {
// let the command handle the completion // let the command handle the completion
return false return false
} }
@ -407,13 +463,8 @@ func checkCompletions(cCtx *Context) bool {
return true return true
} }
func checkCommandCompletions(c *Context, name string) bool { func subtract(a, b int) int {
if !c.shellComplete { return a - b
return false
}
ShowCommandCompletions(c, name)
return true
} }
func indent(spaces int, v string) string { func indent(spaces int, v string) string {
@ -426,25 +477,28 @@ func nindent(spaces int, v string) string {
} }
func wrap(input string, offset int, wrapAt int) string { func wrap(input string, offset int, wrapAt int) string {
var sb strings.Builder var ss []string
lines := strings.Split(input, "\n") lines := strings.Split(input, "\n")
padding := strings.Repeat(" ", offset) padding := strings.Repeat(" ", offset)
for i, line := range lines { for i, line := range lines {
if i != 0 { if line == "" {
sb.WriteString(padding) ss = append(ss, line)
} } else {
wrapped := wrapLine(line, offset, wrapAt, padding)
if i == 0 {
ss = append(ss, wrapped)
} else {
ss = append(ss, padding+wrapped)
sb.WriteString(wrapLine(line, offset, wrapAt, padding)) }
if i != len(lines)-1 {
sb.WriteString("\n")
} }
} }
return sb.String() return strings.Join(ss, "\n")
} }
func wrapLine(input string, offset int, wrapAt int, padding string) string { func wrapLine(input string, offset int, wrapAt int, padding string) string {
@ -476,3 +530,28 @@ func wrapLine(input string, offset int, wrapAt int, padding string) string {
func offset(input string, fixed int) int { func offset(input string, fixed int) int {
return len(input) + fixed return len(input) + fixed
} }
// this function tries to find the max width of the names column
// so say we have the following rows for help
//
// foo1, foo2, foo3 some string here
// bar1, b2 some other string here
//
// We want to offset the 2nd row usage by some amount so that everything
// is aligned
//
// foo1, foo2, foo3 some string here
// bar1, b2 some other string here
//
// to find that offset we find the length of all the rows and use the max
// to calculate the offset
func offsetCommands(cmds []*Command, fixed int) int {
var max int = 0
for _, cmd := range cmds {
s := strings.Join(cmd.Names(), ", ")
if len(s) > max {
max = len(s)
}
}
return max + fixed
}

View File

@ -9,9 +9,45 @@ site_url: https://cli.urfave.org/
repo_url: https://github.com/urfave/cli repo_url: https://github.com/urfave/cli
edit_uri: edit/main/docs/ edit_uri: edit/main/docs/
nav: nav:
- Home: index.md - Home:
- v2 Manual: v2/index.md - Welcome: index.md
- v1 Manual: v1/index.md - Contributing: CONTRIBUTING.md
- Code of Conduct: CODE_OF_CONDUCT.md
- Releasing: RELEASING.md
- Security: SECURITY.md
- Migrate v1 to v2: migrate-v1-to-v2.md
- v2 Manual:
- Getting Started: v2/getting-started.md
- Migrating From Older Releases: v2/migrating-from-older-releases.md
- Examples:
- Greet: v2/examples/greet.md
- Arguments: v2/examples/arguments.md
- Flags: v2/examples/flags.md
- Subcommands: v2/examples/subcommands.md
- Subcommands Categories: v2/examples/subcommands-categories.md
- Exit Codes: v2/examples/exit-codes.md
- Combining Short Options: v2/examples/combining-short-options.md
- Bash Completions: v2/examples/bash-completions.md
- Generated Help Text: v2/examples/generated-help-text.md
- Version Flag: v2/examples/version-flag.md
- Timestamp Flag: v2/examples/timestamp-flag.md
- Suggestions: v2/examples/suggestions.md
- Full API Example: v2/examples/full-api-example.md
- v1 Manual:
- Getting Started: v1/getting-started.md
- Migrating to v2: v1/migrating-to-v2.md
- Examples:
- Greet: v1/examples/greet.md
- Arguments: v1/examples/arguments.md
- Flags: v1/examples/flags.md
- Subcommands: v1/examples/subcommands.md
- Subcommands (Categories): v1/examples/subcommands-categories.md
- Exit Codes: v1/examples/exit-codes.md
- Combining Short Options: v1/examples/combining-short-options.md
- Bash Completions: v1/examples/bash-completions.md
- Generated Help Text: v1/examples/generated-help-text.md
- Version Flag: v1/examples/version-flag.md
theme: theme:
name: material name: material
palette: palette:
@ -25,9 +61,18 @@ theme:
toggle: toggle:
icon: material/brightness-7 icon: material/brightness-7
name: light mode name: light mode
features:
- content.code.annotate
- navigation.top
- navigation.instant
- navigation.expand
- navigation.sections
- navigation.tabs
- navigation.tabs.sticky
plugins: plugins:
- git-revision-date-localized - git-revision-date-localized
- search - search
- tags
# NOTE: this is the recommended configuration from # NOTE: this is the recommended configuration from
# https://squidfunk.github.io/mkdocs-material/setup/extensions/#recommended-configuration # https://squidfunk.github.io/mkdocs-material/setup/extensions/#recommended-configuration
markdown_extensions: markdown_extensions:

View File

@ -46,7 +46,10 @@ func parseIter(set *flag.FlagSet, ip iterativeParser, args []string, shellComple
} }
// swap current argument with the split version // swap current argument with the split version
args = append(args[:i], append(shortOpts, args[i+1:]...)...) // do not include args that parsed correctly so far as it would
// trigger Value.Set() on those args and would result in
// duplicates for slice type flags
args = append(shortOpts, args[i+1:]...)
argsWereSplit = true argsWereSplit = true
break break
} }
@ -56,13 +59,6 @@ func parseIter(set *flag.FlagSet, ip iterativeParser, args []string, shellComple
if !argsWereSplit { if !argsWereSplit {
return err return err
} }
// Since custom parsing failed, replace the flag set before retrying
newSet, err := ip.newFlagSet()
if err != nil {
return err
}
*set = *newSet
} }
} }

View File

@ -1,10 +1,38 @@
package cli package cli
var helpNameTemplate = `{{$v := offset .HelpName 6}}{{wrap .HelpName 3}}{{if .Usage}} - {{wrap .Usage $v}}{{end}}`
var usageTemplate = `{{if .UsageText}}{{wrap .UsageText 3}}{{else}}{{.HelpName}}{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}`
var descriptionTemplate = `{{wrap .Description 3}}`
var authorsTemplate = `{{with $length := len .Authors}}{{if ne 1 $length}}S{{end}}{{end}}:
{{range $index, $author := .Authors}}{{if $index}}
{{end}}{{$author}}{{end}}`
var visibleCommandTemplate = `{{ $cv := offsetCommands .VisibleCommands 5}}{{range .VisibleCommands}}
{{$s := join .Names ", "}}{{$s}}{{ $sp := subtract $cv (offset $s 3) }}{{ indent $sp ""}}{{wrap .Usage $cv}}{{end}}`
var visibleCommandCategoryTemplate = `{{range .VisibleCategories}}{{if .Name}}
{{.Name}}:{{range .VisibleCommands}}
{{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{else}}{{template "visibleCommandTemplate" .}}{{end}}{{end}}`
var visibleFlagCategoryTemplate = `{{range .VisibleFlagCategories}}
{{if .Name}}{{.Name}}
{{end}}{{$flglen := len .Flags}}{{range $i, $e := .Flags}}{{if eq (subtract $flglen $i) 1}}{{$e}}
{{else}}{{$e}}
{{end}}{{end}}{{end}}`
var visibleFlagTemplate = `{{range $i, $e := .VisibleFlags}}
{{wrap $e.String 6}}{{end}}`
var versionTemplate = `{{if .Version}}{{if not .HideVersion}}
VERSION:
{{.Version}}{{end}}{{end}}`
var copyrightTemplate = `{{wrap .Copyright 3}}`
// AppHelpTemplate is the text template for the Default help topic. // AppHelpTemplate is the text template for the Default help topic.
// cli.go uses text/template to render templates. You can // cli.go uses text/template to render templates. You can
// render custom help text by setting this variable. // render custom help text by setting this variable.
var AppHelpTemplate = `NAME: var AppHelpTemplate = `NAME:
{{$v := offset .Name 6}}{{wrap .Name 3}}{{if .Usage}} - {{wrap .Usage $v}}{{end}} {{template "helpNameTemplate" .}}
USAGE: USAGE:
{{if .UsageText}}{{wrap .UsageText 3}}{{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}} {{if .UsageText}}{{wrap .UsageText 3}}{{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}}
@ -13,75 +41,58 @@ VERSION:
{{.Version}}{{end}}{{end}}{{if .Description}} {{.Version}}{{end}}{{end}}{{if .Description}}
DESCRIPTION: DESCRIPTION:
{{wrap .Description 3}}{{end}}{{if len .Authors}} {{template "descriptionTemplate" .}}{{end}}
{{- if len .Authors}}
AUTHOR{{with $length := len .Authors}}{{if ne 1 $length}}S{{end}}{{end}}: AUTHOR{{template "authorsTemplate" .}}{{end}}{{if .VisibleCommands}}
{{range $index, $author := .Authors}}{{if $index}}
{{end}}{{$author}}{{end}}{{end}}{{if .VisibleCommands}}
COMMANDS:{{range .VisibleCategories}}{{if .Name}} COMMANDS:{{template "visibleCommandCategoryTemplate" .}}{{end}}{{if .VisibleFlagCategories}}
{{.Name}}:{{range .VisibleCommands}}
{{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{else}}{{range .VisibleCommands}}
{{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{end}}{{end}}{{end}}{{if .VisibleFlagCategories}}
GLOBAL OPTIONS:{{range .VisibleFlagCategories}} GLOBAL OPTIONS:{{template "visibleFlagCategoryTemplate" .}}{{else if .VisibleFlags}}
{{if .Name}}{{.Name}}
{{end}}{{range .Flags}}{{.}}
{{end}}{{end}}{{else}}{{if .VisibleFlags}}
GLOBAL OPTIONS: GLOBAL OPTIONS:{{template "visibleFlagTemplate" .}}{{end}}{{if .Copyright}}
{{range $index, $option := .VisibleFlags}}{{if $index}}
{{end}}{{wrap $option.String 6}}{{end}}{{end}}{{end}}{{if .Copyright}}
COPYRIGHT: COPYRIGHT:
{{wrap .Copyright 3}}{{end}} {{template "copyrightTemplate" .}}{{end}}
` `
// CommandHelpTemplate is the text template for the command help topic. // CommandHelpTemplate is the text template for the command help topic.
// cli.go uses text/template to render templates. You can // cli.go uses text/template to render templates. You can
// render custom help text by setting this variable. // render custom help text by setting this variable.
var CommandHelpTemplate = `NAME: var CommandHelpTemplate = `NAME:
{{$v := offset .HelpName 6}}{{wrap .HelpName 3}}{{if .Usage}} - {{wrap .Usage $v}}{{end}} {{template "helpNameTemplate" .}}
USAGE: USAGE:
{{if .UsageText}}{{wrap .UsageText 3}}{{else}}{{.HelpName}}{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Category}} {{template "usageTemplate" .}}{{if .Category}}
CATEGORY: CATEGORY:
{{.Category}}{{end}}{{if .Description}} {{.Category}}{{end}}{{if .Description}}
DESCRIPTION: DESCRIPTION:
{{wrap .Description 3}}{{end}}{{if .VisibleFlagCategories}} {{template "descriptionTemplate" .}}{{end}}{{if .VisibleFlagCategories}}
OPTIONS:{{range .VisibleFlagCategories}} OPTIONS:{{template "visibleFlagCategoryTemplate" .}}{{else if .VisibleFlags}}
{{if .Name}}{{.Name}}
{{end}}{{range .Flags}}{{.}}
{{end}}{{end}}{{else}}{{if .VisibleFlags}}
OPTIONS: OPTIONS:{{template "visibleFlagTemplate" .}}{{end}}
{{range .VisibleFlags}}{{.}}
{{end}}{{end}}{{end}}
` `
// SubcommandHelpTemplate is the text template for the subcommand help topic. // SubcommandHelpTemplate is the text template for the subcommand help topic.
// cli.go uses text/template to render templates. You can // cli.go uses text/template to render templates. You can
// render custom help text by setting this variable. // render custom help text by setting this variable.
var SubcommandHelpTemplate = `NAME: var SubcommandHelpTemplate = `NAME:
{{.HelpName}} - {{.Usage}} {{template "helpNameTemplate" .}}
USAGE: USAGE:
{{if .UsageText}}{{wrap .UsageText 3}}{{else}}{{.HelpName}} command{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Description}} {{if .UsageText}}{{wrap .UsageText 3}}{{else}}{{.HelpName}} {{if .VisibleFlags}}command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Description}}
DESCRIPTION: DESCRIPTION:
{{wrap .Description 3}}{{end}} {{template "descriptionTemplate" .}}{{end}}{{if .VisibleCommands}}
COMMANDS:{{range .VisibleCategories}}{{if .Name}} COMMANDS:{{template "visibleCommandTemplate" .}}{{end}}{{if .VisibleFlagCategories}}
{{.Name}}:{{range .VisibleCommands}}
{{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{else}}{{range .VisibleCommands}}
{{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{end}}{{end}}{{if .VisibleFlags}}
OPTIONS: OPTIONS:{{template "visibleFlagCategoryTemplate" .}}{{else if .VisibleFlags}}
{{range .VisibleFlags}}{{.}}
{{end}}{{end}} OPTIONS:{{template "visibleFlagTemplate" .}}{{end}}
` `
var MarkdownDocTemplate = `{{if gt .SectionNum 0}}% {{ .App.Name }} {{ .SectionNum }} var MarkdownDocTemplate = `{{if gt .SectionNum 0}}% {{ .App.Name }} {{ .SectionNum }}

View File

@ -22,6 +22,12 @@ type Float64SliceFlag struct {
Aliases []string Aliases []string
EnvVars []string EnvVars []string
defaultValue *Float64Slice
separator separatorSpec
Action func(*Context, []float64) error
} }
// IsSet returns whether or not the flag has been set through env or file // IsSet returns whether or not the flag has been set through env or file
@ -58,12 +64,16 @@ type GenericFlag struct {
HasBeenSet bool HasBeenSet bool
Value Generic Value Generic
Destination *Generic Destination Generic
Aliases []string Aliases []string
EnvVars []string EnvVars []string
defaultValue Generic
TakesFile bool TakesFile bool
Action func(*Context, interface{}) error
} }
// String returns a readable representation of this value (for usage defaults) // String returns a readable representation of this value (for usage defaults)
@ -109,6 +119,12 @@ type Int64SliceFlag struct {
Aliases []string Aliases []string
EnvVars []string EnvVars []string
defaultValue *Int64Slice
separator separatorSpec
Action func(*Context, []int64) error
} }
// IsSet returns whether or not the flag has been set through env or file // IsSet returns whether or not the flag has been set through env or file
@ -149,6 +165,12 @@ type IntSliceFlag struct {
Aliases []string Aliases []string
EnvVars []string EnvVars []string
defaultValue *IntSlice
separator separatorSpec
Action func(*Context, []int) error
} }
// IsSet returns whether or not the flag has been set through env or file // IsSet returns whether or not the flag has been set through env or file
@ -190,7 +212,11 @@ type PathFlag struct {
Aliases []string Aliases []string
EnvVars []string EnvVars []string
defaultValue Path
TakesFile bool TakesFile bool
Action func(*Context, Path) error
} }
// String returns a readable representation of this value (for usage defaults) // String returns a readable representation of this value (for usage defaults)
@ -237,7 +263,15 @@ type StringSliceFlag struct {
Aliases []string Aliases []string
EnvVars []string EnvVars []string
defaultValue *StringSlice
separator separatorSpec
TakesFile bool TakesFile bool
Action func(*Context, []string) error
KeepSpace bool
} }
// IsSet returns whether or not the flag has been set through env or file // IsSet returns whether or not the flag has been set through env or file
@ -279,7 +313,13 @@ type TimestampFlag struct {
Aliases []string Aliases []string
EnvVars []string EnvVars []string
defaultValue *Timestamp
Layout string Layout string
Timezone *time.Location
Action func(*Context, *time.Time) error
} }
// String returns a readable representation of this value (for usage defaults) // String returns a readable representation of this value (for usage defaults)
@ -307,6 +347,98 @@ func (f *TimestampFlag) IsVisible() bool {
return !f.Hidden return !f.Hidden
} }
// Uint64SliceFlag is a flag with type *Uint64Slice
type Uint64SliceFlag struct {
Name string
Category string
DefaultText string
FilePath string
Usage string
Required bool
Hidden bool
HasBeenSet bool
Value *Uint64Slice
Destination *Uint64Slice
Aliases []string
EnvVars []string
defaultValue *Uint64Slice
separator separatorSpec
Action func(*Context, []uint64) error
}
// IsSet returns whether or not the flag has been set through env or file
func (f *Uint64SliceFlag) IsSet() bool {
return f.HasBeenSet
}
// Names returns the names of the flag
func (f *Uint64SliceFlag) Names() []string {
return FlagNames(f.Name, f.Aliases)
}
// IsRequired returns whether or not the flag is required
func (f *Uint64SliceFlag) IsRequired() bool {
return f.Required
}
// IsVisible returns true if the flag is not hidden, otherwise false
func (f *Uint64SliceFlag) IsVisible() bool {
return !f.Hidden
}
// UintSliceFlag is a flag with type *UintSlice
type UintSliceFlag struct {
Name string
Category string
DefaultText string
FilePath string
Usage string
Required bool
Hidden bool
HasBeenSet bool
Value *UintSlice
Destination *UintSlice
Aliases []string
EnvVars []string
defaultValue *UintSlice
separator separatorSpec
Action func(*Context, []uint) error
}
// IsSet returns whether or not the flag has been set through env or file
func (f *UintSliceFlag) IsSet() bool {
return f.HasBeenSet
}
// Names returns the names of the flag
func (f *UintSliceFlag) Names() []string {
return FlagNames(f.Name, f.Aliases)
}
// IsRequired returns whether or not the flag is required
func (f *UintSliceFlag) IsRequired() bool {
return f.Required
}
// IsVisible returns true if the flag is not hidden, otherwise false
func (f *UintSliceFlag) IsVisible() bool {
return !f.Hidden
}
// BoolFlag is a flag with type bool // BoolFlag is a flag with type bool
type BoolFlag struct { type BoolFlag struct {
Name string Name string
@ -325,6 +457,14 @@ type BoolFlag struct {
Aliases []string Aliases []string
EnvVars []string EnvVars []string
defaultValue bool
Count *int
DisableDefaultText bool
Action func(*Context, bool) error
} }
// String returns a readable representation of this value (for usage defaults) // String returns a readable representation of this value (for usage defaults)
@ -370,6 +510,10 @@ type Float64Flag struct {
Aliases []string Aliases []string
EnvVars []string EnvVars []string
defaultValue float64
Action func(*Context, float64) error
} }
// String returns a readable representation of this value (for usage defaults) // String returns a readable representation of this value (for usage defaults)
@ -415,6 +559,12 @@ type IntFlag struct {
Aliases []string Aliases []string
EnvVars []string EnvVars []string
defaultValue int
Base int
Action func(*Context, int) error
} }
// String returns a readable representation of this value (for usage defaults) // String returns a readable representation of this value (for usage defaults)
@ -460,6 +610,12 @@ type Int64Flag struct {
Aliases []string Aliases []string
EnvVars []string EnvVars []string
defaultValue int64
Base int
Action func(*Context, int64) error
} }
// String returns a readable representation of this value (for usage defaults) // String returns a readable representation of this value (for usage defaults)
@ -506,7 +662,11 @@ type StringFlag struct {
Aliases []string Aliases []string
EnvVars []string EnvVars []string
defaultValue string
TakesFile bool TakesFile bool
Action func(*Context, string) error
} }
// String returns a readable representation of this value (for usage defaults) // String returns a readable representation of this value (for usage defaults)
@ -552,6 +712,10 @@ type DurationFlag struct {
Aliases []string Aliases []string
EnvVars []string EnvVars []string
defaultValue time.Duration
Action func(*Context, time.Duration) error
} }
// String returns a readable representation of this value (for usage defaults) // String returns a readable representation of this value (for usage defaults)
@ -597,6 +761,12 @@ type UintFlag struct {
Aliases []string Aliases []string
EnvVars []string EnvVars []string
defaultValue uint
Base int
Action func(*Context, uint) error
} }
// String returns a readable representation of this value (for usage defaults) // String returns a readable representation of this value (for usage defaults)
@ -642,6 +812,12 @@ type Uint64Flag struct {
Aliases []string Aliases []string
EnvVars []string EnvVars []string
defaultValue uint64
Base int
Action func(*Context, uint64) error
} }
// String returns a readable representation of this value (for usage defaults) // String returns a readable representation of this value (for usage defaults)

2
vendor/modules.txt vendored
View File

@ -19,7 +19,7 @@ github.com/russross/blackfriday/v2
# github.com/sirupsen/logrus v1.8.1 # github.com/sirupsen/logrus v1.8.1
## explicit; go 1.13 ## explicit; go 1.13
github.com/sirupsen/logrus github.com/sirupsen/logrus
# github.com/urfave/cli/v2 v2.10.3 # github.com/urfave/cli/v2 v2.24.4
## explicit; go 1.18 ## explicit; go 1.18
github.com/urfave/cli/v2 github.com/urfave/cli/v2
# github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 # github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673