mirror of
https://github.com/NVIDIA/nvidia-container-toolkit
synced 2025-06-26 18:18:24 +00:00
[no-relnote] Migrate to urfave v3
Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>
This commit is contained in:
563
vendor/github.com/urfave/cli/v3/command.go
generated
vendored
Normal file
563
vendor/github.com/urfave/cli/v3/command.go
generated
vendored
Normal file
@@ -0,0 +1,563 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"slices"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
// ignoreFlagPrefix is to ignore test flags when adding flags from other packages
|
||||
ignoreFlagPrefix = "test."
|
||||
|
||||
commandContextKey = contextKey("cli.context")
|
||||
)
|
||||
|
||||
type contextKey string
|
||||
|
||||
// Command contains everything needed to run an application that
|
||||
// accepts a string slice of arguments such as os.Args. A given
|
||||
// Command may contain Flags and sub-commands in Commands.
|
||||
type Command struct {
|
||||
// The name of the command
|
||||
Name string `json:"name"`
|
||||
// A list of aliases for the command
|
||||
Aliases []string `json:"aliases"`
|
||||
// A short description of the usage of this command
|
||||
Usage string `json:"usage"`
|
||||
// Text to override the USAGE section of help
|
||||
UsageText string `json:"usageText"`
|
||||
// A short description of the arguments of this command
|
||||
ArgsUsage string `json:"argsUsage"`
|
||||
// Version of the command
|
||||
Version string `json:"version"`
|
||||
// Longer explanation of how the command works
|
||||
Description string `json:"description"`
|
||||
// DefaultCommand is the (optional) name of a command
|
||||
// to run if no command names are passed as CLI arguments.
|
||||
DefaultCommand string `json:"defaultCommand"`
|
||||
// The category the command is part of
|
||||
Category string `json:"category"`
|
||||
// List of child commands
|
||||
Commands []*Command `json:"commands"`
|
||||
// List of flags to parse
|
||||
Flags []Flag `json:"flags"`
|
||||
// Boolean to hide built-in help command and help flag
|
||||
HideHelp bool `json:"hideHelp"`
|
||||
// Ignored if HideHelp is true.
|
||||
HideHelpCommand bool `json:"hideHelpCommand"`
|
||||
// Boolean to hide built-in version flag and the VERSION section of help
|
||||
HideVersion bool `json:"hideVersion"`
|
||||
// Boolean to enable shell completion commands
|
||||
EnableShellCompletion bool `json:"-"`
|
||||
// Shell Completion generation command name
|
||||
ShellCompletionCommandName string `json:"-"`
|
||||
// The function to call when checking for shell command completions
|
||||
ShellComplete ShellCompleteFunc `json:"-"`
|
||||
// The function to configure a shell completion command
|
||||
ConfigureShellCompletionCommand ConfigureShellCompletionCommand `json:"-"`
|
||||
// An action to execute before any subcommands are run, but after the context is ready
|
||||
// If a non-nil error is returned, no subcommands are run
|
||||
Before BeforeFunc `json:"-"`
|
||||
// An action to execute after any subcommands are run, but after the subcommand has finished
|
||||
// It is run even if Action() panics
|
||||
After AfterFunc `json:"-"`
|
||||
// The function to call when this command is invoked
|
||||
Action ActionFunc `json:"-"`
|
||||
// Execute this function if the proper command cannot be found
|
||||
CommandNotFound CommandNotFoundFunc `json:"-"`
|
||||
// Execute this function if a usage error occurs.
|
||||
OnUsageError OnUsageErrorFunc `json:"-"`
|
||||
// Execute this function when an invalid flag is accessed from the context
|
||||
InvalidFlagAccessHandler InvalidFlagAccessFunc `json:"-"`
|
||||
// Boolean to hide this command from help or completion
|
||||
Hidden bool `json:"hidden"`
|
||||
// List of all authors who contributed (string or fmt.Stringer)
|
||||
// TODO: ~string | fmt.Stringer when interface unions are available
|
||||
Authors []any `json:"authors"`
|
||||
// Copyright of the binary if any
|
||||
Copyright string `json:"copyright"`
|
||||
// Reader reader to write input to (useful for tests)
|
||||
Reader io.Reader `json:"-"`
|
||||
// Writer writer to write output to
|
||||
Writer io.Writer `json:"-"`
|
||||
// ErrWriter writes error output
|
||||
ErrWriter io.Writer `json:"-"`
|
||||
// ExitErrHandler processes any error encountered while running an App before
|
||||
// it is returned to the caller. If no function is provided, HandleExitCoder
|
||||
// is used as the default behavior.
|
||||
ExitErrHandler ExitErrHandlerFunc `json:"-"`
|
||||
// Other custom info
|
||||
Metadata map[string]interface{} `json:"metadata"`
|
||||
// Carries a function which returns app specific info.
|
||||
ExtraInfo func() map[string]string `json:"-"`
|
||||
// CustomRootCommandHelpTemplate the text template for app help topic.
|
||||
// cli.go uses text/template to render templates. You can
|
||||
// render custom help text by setting this variable.
|
||||
CustomRootCommandHelpTemplate string `json:"-"`
|
||||
// SliceFlagSeparator is used to customize the separator for SliceFlag, the default is ","
|
||||
SliceFlagSeparator string `json:"sliceFlagSeparator"`
|
||||
// DisableSliceFlagSeparator is used to disable SliceFlagSeparator, the default is false
|
||||
DisableSliceFlagSeparator bool `json:"disableSliceFlagSeparator"`
|
||||
// Boolean to enable short-option handling so user can combine several
|
||||
// single-character bool arguments into one
|
||||
// i.e. foobar -o -v -> foobar -ov
|
||||
UseShortOptionHandling bool `json:"useShortOptionHandling"`
|
||||
// Enable suggestions for commands and flags
|
||||
Suggest bool `json:"suggest"`
|
||||
// Allows global flags set by libraries which use flag.XXXVar(...) directly
|
||||
// to be parsed through this library
|
||||
AllowExtFlags bool `json:"allowExtFlags"`
|
||||
// Treat all flags as normal arguments if true
|
||||
SkipFlagParsing bool `json:"skipFlagParsing"`
|
||||
// CustomHelpTemplate the text template for the command help topic.
|
||||
// cli.go uses text/template to render templates. You can
|
||||
// render custom help text by setting this variable.
|
||||
CustomHelpTemplate string `json:"-"`
|
||||
// Use longest prefix match for commands
|
||||
PrefixMatchCommands bool `json:"prefixMatchCommands"`
|
||||
// Custom suggest command for matching
|
||||
SuggestCommandFunc SuggestCommandFunc `json:"-"`
|
||||
// Flag exclusion group
|
||||
MutuallyExclusiveFlags []MutuallyExclusiveFlags `json:"mutuallyExclusiveFlags"`
|
||||
// Arguments to parse for this command
|
||||
Arguments []Argument `json:"arguments"`
|
||||
// Whether to read arguments from stdin
|
||||
// applicable to root command only
|
||||
ReadArgsFromStdin bool `json:"readArgsFromStdin"`
|
||||
|
||||
// categories contains the categorized commands and is populated on app startup
|
||||
categories CommandCategories
|
||||
// flagCategories contains the categorized flags and is populated on app startup
|
||||
flagCategories FlagCategories
|
||||
// flags that have been applied in current parse
|
||||
appliedFlags []Flag
|
||||
// flags that have been set
|
||||
setFlags map[Flag]struct{}
|
||||
// The parent of this command. This value will be nil for the
|
||||
// command at the root of the graph.
|
||||
parent *Command
|
||||
// parsed args
|
||||
parsedArgs Args
|
||||
// track state of error handling
|
||||
isInError bool
|
||||
// track state of defaults
|
||||
didSetupDefaults bool
|
||||
// whether in shell completion mode
|
||||
shellCompletion bool
|
||||
}
|
||||
|
||||
// FullName returns the full name of the command.
|
||||
// For commands with parents this ensures that the parent commands
|
||||
// are part of the command path.
|
||||
func (cmd *Command) FullName() string {
|
||||
namePath := []string{}
|
||||
|
||||
if cmd.parent != nil {
|
||||
namePath = append(namePath, cmd.parent.FullName())
|
||||
}
|
||||
|
||||
return strings.Join(append(namePath, cmd.Name), " ")
|
||||
}
|
||||
|
||||
func (cmd *Command) Command(name string) *Command {
|
||||
for _, subCmd := range cmd.Commands {
|
||||
if subCmd.HasName(name) {
|
||||
return subCmd
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cmd *Command) checkHelp() bool {
|
||||
tracef("checking if help is wanted (cmd=%[1]q)", cmd.Name)
|
||||
|
||||
return HelpFlag != nil && slices.ContainsFunc(HelpFlag.Names(), cmd.Bool)
|
||||
}
|
||||
|
||||
func (cmd *Command) allFlags() []Flag {
|
||||
var flags []Flag
|
||||
flags = append(flags, cmd.Flags...)
|
||||
for _, grpf := range cmd.MutuallyExclusiveFlags {
|
||||
for _, f1 := range grpf.Flags {
|
||||
flags = append(flags, f1...)
|
||||
}
|
||||
}
|
||||
return flags
|
||||
}
|
||||
|
||||
// useShortOptionHandling traverses Lineage() for *any* ancestors
|
||||
// with UseShortOptionHandling
|
||||
func (cmd *Command) useShortOptionHandling() bool {
|
||||
for _, pCmd := range cmd.Lineage() {
|
||||
if pCmd.UseShortOptionHandling {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (cmd *Command) suggestFlagFromError(err error, commandName string) (string, error) {
|
||||
fl, parseErr := flagFromError(err)
|
||||
if parseErr != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
flags := cmd.Flags
|
||||
hideHelp := cmd.hideHelp()
|
||||
|
||||
if commandName != "" {
|
||||
subCmd := cmd.Command(commandName)
|
||||
if subCmd == nil {
|
||||
return "", err
|
||||
}
|
||||
flags = subCmd.Flags
|
||||
hideHelp = hideHelp || subCmd.HideHelp
|
||||
}
|
||||
|
||||
suggestion := SuggestFlag(flags, fl, hideHelp)
|
||||
if len(suggestion) == 0 {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return fmt.Sprintf(SuggestDidYouMeanTemplate, suggestion) + "\n\n", nil
|
||||
}
|
||||
|
||||
// Names returns the names including short names and aliases.
|
||||
func (cmd *Command) Names() []string {
|
||||
return append([]string{cmd.Name}, cmd.Aliases...)
|
||||
}
|
||||
|
||||
// HasName returns true if Command.Name matches given name
|
||||
func (cmd *Command) HasName(name string) bool {
|
||||
return slices.Contains(cmd.Names(), name)
|
||||
}
|
||||
|
||||
// VisibleCategories returns a slice of categories and commands that are
|
||||
// Hidden=false
|
||||
func (cmd *Command) VisibleCategories() []CommandCategory {
|
||||
ret := []CommandCategory{}
|
||||
for _, category := range cmd.categories.Categories() {
|
||||
if visible := func() CommandCategory {
|
||||
if len(category.VisibleCommands()) > 0 {
|
||||
return category
|
||||
}
|
||||
return nil
|
||||
}(); visible != nil {
|
||||
ret = append(ret, visible)
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// VisibleCommands returns a slice of the Commands with Hidden=false
|
||||
func (cmd *Command) VisibleCommands() []*Command {
|
||||
var ret []*Command
|
||||
for _, command := range cmd.Commands {
|
||||
if command.Hidden || command.Name == helpName {
|
||||
continue
|
||||
}
|
||||
ret = append(ret, command)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// VisibleFlagCategories returns a slice containing all the visible flag categories with the flags they contain
|
||||
func (cmd *Command) VisibleFlagCategories() []VisibleFlagCategory {
|
||||
if cmd.flagCategories == nil {
|
||||
cmd.flagCategories = newFlagCategoriesFromFlags(cmd.allFlags())
|
||||
}
|
||||
return cmd.flagCategories.VisibleCategories()
|
||||
}
|
||||
|
||||
// VisibleFlags returns a slice of the Flags with Hidden=false
|
||||
func (cmd *Command) VisibleFlags() []Flag {
|
||||
return visibleFlags(cmd.allFlags())
|
||||
}
|
||||
|
||||
func (cmd *Command) appendFlag(fl Flag) {
|
||||
if !hasFlag(cmd.Flags, fl) {
|
||||
cmd.Flags = append(cmd.Flags, fl)
|
||||
}
|
||||
}
|
||||
|
||||
// VisiblePersistentFlags returns a slice of [LocalFlag] with Persistent=true and Hidden=false.
|
||||
func (cmd *Command) VisiblePersistentFlags() []Flag {
|
||||
var flags []Flag
|
||||
for _, fl := range cmd.Root().Flags {
|
||||
pfl, ok := fl.(LocalFlag)
|
||||
if !ok || pfl.IsLocal() {
|
||||
continue
|
||||
}
|
||||
flags = append(flags, fl)
|
||||
}
|
||||
return visibleFlags(flags)
|
||||
}
|
||||
|
||||
func (cmd *Command) appendCommand(aCmd *Command) {
|
||||
if !slices.Contains(cmd.Commands, aCmd) {
|
||||
aCmd.parent = cmd
|
||||
cmd.Commands = append(cmd.Commands, aCmd)
|
||||
}
|
||||
}
|
||||
|
||||
func (cmd *Command) handleExitCoder(ctx context.Context, err error) error {
|
||||
if cmd.parent != nil {
|
||||
return cmd.parent.handleExitCoder(ctx, err)
|
||||
}
|
||||
|
||||
if cmd.ExitErrHandler != nil {
|
||||
cmd.ExitErrHandler(ctx, cmd, err)
|
||||
return err
|
||||
}
|
||||
|
||||
HandleExitCoder(err)
|
||||
return err
|
||||
}
|
||||
|
||||
func (cmd *Command) argsWithDefaultCommand(oldArgs Args) Args {
|
||||
if cmd.DefaultCommand != "" {
|
||||
rawArgs := append([]string{cmd.DefaultCommand}, oldArgs.Slice()...)
|
||||
newArgs := &stringSliceArgs{v: rawArgs}
|
||||
|
||||
return newArgs
|
||||
}
|
||||
|
||||
return oldArgs
|
||||
}
|
||||
|
||||
// Root returns the Command at the root of the graph
|
||||
func (cmd *Command) Root() *Command {
|
||||
if cmd.parent == nil {
|
||||
return cmd
|
||||
}
|
||||
|
||||
return cmd.parent.Root()
|
||||
}
|
||||
|
||||
func (cmd *Command) set(fName string, f Flag, val string) error {
|
||||
cmd.setFlags[f] = struct{}{}
|
||||
if err := f.Set(fName, val); err != nil {
|
||||
return fmt.Errorf("invalid value %q for flag -%s: %v", val, fName, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cmd *Command) lFlag(name string) Flag {
|
||||
for _, f := range cmd.allFlags() {
|
||||
if slices.Contains(f.Names(), name) {
|
||||
tracef("flag found for name %[1]q (cmd=%[2]q)", name, cmd.Name)
|
||||
return f
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cmd *Command) lookupFlag(name string) Flag {
|
||||
for _, pCmd := range cmd.Lineage() {
|
||||
if f := pCmd.lFlag(name); f != nil {
|
||||
return f
|
||||
}
|
||||
}
|
||||
|
||||
tracef("flag NOT found for name %[1]q (cmd=%[2]q)", name, cmd.Name)
|
||||
cmd.onInvalidFlag(context.TODO(), name)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cmd *Command) checkRequiredFlag(f Flag) (bool, string) {
|
||||
if rf, ok := f.(RequiredFlag); ok && rf.IsRequired() {
|
||||
flagName := f.Names()[0]
|
||||
if !f.IsSet() {
|
||||
return false, flagName
|
||||
}
|
||||
}
|
||||
return true, ""
|
||||
}
|
||||
|
||||
func (cmd *Command) checkAllRequiredFlags() requiredFlagsErr {
|
||||
for pCmd := cmd; pCmd != nil; pCmd = pCmd.parent {
|
||||
if err := pCmd.checkRequiredFlags(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cmd *Command) checkRequiredFlags() requiredFlagsErr {
|
||||
tracef("checking for required flags (cmd=%[1]q)", cmd.Name)
|
||||
|
||||
missingFlags := []string{}
|
||||
|
||||
for _, f := range cmd.appliedFlags {
|
||||
if ok, name := cmd.checkRequiredFlag(f); !ok {
|
||||
missingFlags = append(missingFlags, name)
|
||||
}
|
||||
}
|
||||
|
||||
if len(missingFlags) != 0 {
|
||||
tracef("found missing required flags %[1]q (cmd=%[2]q)", missingFlags, cmd.Name)
|
||||
|
||||
return &errRequiredFlags{missingFlags: missingFlags}
|
||||
}
|
||||
|
||||
tracef("all required flags set (cmd=%[1]q)", cmd.Name)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cmd *Command) onInvalidFlag(ctx context.Context, name string) {
|
||||
for cmd != nil {
|
||||
if cmd.InvalidFlagAccessHandler != nil {
|
||||
cmd.InvalidFlagAccessHandler(ctx, cmd, name)
|
||||
break
|
||||
}
|
||||
cmd = cmd.parent
|
||||
}
|
||||
}
|
||||
|
||||
// NumFlags returns the number of flags set
|
||||
func (cmd *Command) NumFlags() int {
|
||||
tracef("numFlags numAppliedFlags %d", len(cmd.appliedFlags))
|
||||
count := 0
|
||||
for _, f := range cmd.appliedFlags {
|
||||
if f.IsSet() {
|
||||
count++
|
||||
}
|
||||
}
|
||||
return count // cmd.flagSet.NFlag()
|
||||
}
|
||||
|
||||
// Set sets a context flag to a value.
|
||||
func (cmd *Command) Set(name, value string) error {
|
||||
if f := cmd.lookupFlag(name); f != nil {
|
||||
return f.Set(name, value)
|
||||
}
|
||||
|
||||
return fmt.Errorf("no such flag -%s", name)
|
||||
}
|
||||
|
||||
// IsSet determines if the flag was actually set
|
||||
func (cmd *Command) IsSet(name string) bool {
|
||||
fl := cmd.lookupFlag(name)
|
||||
if fl == nil {
|
||||
tracef("flag with name %[1]q NOT found; assuming not set (cmd=%[2]q)", name, cmd.Name)
|
||||
return false
|
||||
}
|
||||
|
||||
isSet := fl.IsSet()
|
||||
if isSet {
|
||||
tracef("flag with name %[1]q is set (cmd=%[2]q)", name, cmd.Name)
|
||||
} else {
|
||||
tracef("flag with name %[1]q is no set (cmd=%[2]q)", name, cmd.Name)
|
||||
}
|
||||
|
||||
return isSet
|
||||
}
|
||||
|
||||
// LocalFlagNames returns a slice of flag names used in this
|
||||
// command.
|
||||
func (cmd *Command) LocalFlagNames() []string {
|
||||
names := []string{}
|
||||
|
||||
// Check the flags which have been set via env or file
|
||||
for _, f := range cmd.allFlags() {
|
||||
if f.IsSet() {
|
||||
names = append(names, f.Names()...)
|
||||
}
|
||||
}
|
||||
|
||||
// Sort out the duplicates since flag could be set via multiple
|
||||
// paths
|
||||
m := map[string]struct{}{}
|
||||
uniqNames := []string{}
|
||||
|
||||
for _, name := range names {
|
||||
if _, ok := m[name]; !ok {
|
||||
m[name] = struct{}{}
|
||||
uniqNames = append(uniqNames, name)
|
||||
}
|
||||
}
|
||||
|
||||
return uniqNames
|
||||
}
|
||||
|
||||
// FlagNames returns a slice of flag names used by the this command
|
||||
// and all of its parent commands.
|
||||
func (cmd *Command) FlagNames() []string {
|
||||
names := cmd.LocalFlagNames()
|
||||
|
||||
if cmd.parent != nil {
|
||||
names = append(cmd.parent.FlagNames(), names...)
|
||||
}
|
||||
|
||||
return names
|
||||
}
|
||||
|
||||
// Lineage returns *this* command and all of its ancestor commands
|
||||
// in order from child to parent
|
||||
func (cmd *Command) Lineage() []*Command {
|
||||
lineage := []*Command{cmd}
|
||||
|
||||
if cmd.parent != nil {
|
||||
lineage = append(lineage, cmd.parent.Lineage()...)
|
||||
}
|
||||
|
||||
return lineage
|
||||
}
|
||||
|
||||
// Count returns the num of occurrences of this flag
|
||||
func (cmd *Command) Count(name string) int {
|
||||
if cf, ok := cmd.lookupFlag(name).(Countable); ok {
|
||||
return cf.Count()
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Value returns the value of the flag corresponding to `name`
|
||||
func (cmd *Command) Value(name string) interface{} {
|
||||
if fs := cmd.lookupFlag(name); fs != nil {
|
||||
tracef("value found for name %[1]q (cmd=%[2]q)", name, cmd.Name)
|
||||
return fs.Get()
|
||||
}
|
||||
|
||||
tracef("value NOT found for name %[1]q (cmd=%[2]q)", name, cmd.Name)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Args returns the command line arguments associated with the
|
||||
// command.
|
||||
func (cmd *Command) Args() Args {
|
||||
return cmd.parsedArgs
|
||||
}
|
||||
|
||||
// NArg returns the number of the command line arguments.
|
||||
func (cmd *Command) NArg() int {
|
||||
return cmd.Args().Len()
|
||||
}
|
||||
|
||||
func (cmd *Command) runFlagActions(ctx context.Context) error {
|
||||
tracef("runFlagActions")
|
||||
for fl := range cmd.setFlags {
|
||||
/*tracef("checking %v:%v", fl.Names(), fl.IsSet())
|
||||
if !fl.IsSet() {
|
||||
continue
|
||||
}*/
|
||||
|
||||
//if pf, ok := fl.(LocalFlag); ok && !pf.IsLocal() {
|
||||
// continue
|
||||
//}
|
||||
|
||||
if af, ok := fl.(ActionableFlag); ok {
|
||||
if err := af.RunAction(ctx, cmd); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user