...

Source file src/github.com/urfave/cli/v2/context.go

Documentation: github.com/urfave/cli/v2

     1  package cli
     2  
     3  import (
     4  	"context"
     5  	"flag"
     6  	"fmt"
     7  	"strings"
     8  )
     9  
    10  // Context is a type that is passed through to
    11  // each Handler action in a cli application. Context
    12  // can be used to retrieve context-specific args and
    13  // parsed command-line options.
    14  type Context struct {
    15  	context.Context
    16  	App           *App
    17  	Command       *Command
    18  	shellComplete bool
    19  	flagSet       *flag.FlagSet
    20  	parentContext *Context
    21  }
    22  
    23  // NewContext creates a new context. For use in when invoking an App or Command action.
    24  func NewContext(app *App, set *flag.FlagSet, parentCtx *Context) *Context {
    25  	c := &Context{App: app, flagSet: set, parentContext: parentCtx}
    26  	if parentCtx != nil {
    27  		c.Context = parentCtx.Context
    28  		c.shellComplete = parentCtx.shellComplete
    29  		if parentCtx.flagSet == nil {
    30  			parentCtx.flagSet = &flag.FlagSet{}
    31  		}
    32  	}
    33  
    34  	c.Command = &Command{}
    35  
    36  	if c.Context == nil {
    37  		c.Context = context.Background()
    38  	}
    39  
    40  	return c
    41  }
    42  
    43  // NumFlags returns the number of flags set
    44  func (cCtx *Context) NumFlags() int {
    45  	return cCtx.flagSet.NFlag()
    46  }
    47  
    48  // Set sets a context flag to a value.
    49  func (cCtx *Context) Set(name, value string) error {
    50  	if fs := cCtx.lookupFlagSet(name); fs != nil {
    51  		return fs.Set(name, value)
    52  	}
    53  
    54  	return fmt.Errorf("no such flag -%s", name)
    55  }
    56  
    57  // IsSet determines if the flag was actually set
    58  func (cCtx *Context) IsSet(name string) bool {
    59  
    60  	if fs := cCtx.lookupFlagSet(name); fs != nil {
    61  		isSet := false
    62  		fs.Visit(func(f *flag.Flag) {
    63  			if f.Name == name {
    64  				isSet = true
    65  			}
    66  		})
    67  		if isSet {
    68  			return true
    69  		}
    70  
    71  		f := cCtx.lookupFlag(name)
    72  		if f == nil {
    73  			return false
    74  		}
    75  
    76  		if f.IsSet() {
    77  			return true
    78  		}
    79  
    80  		// now redo flagset search on aliases
    81  		aliases := f.Names()
    82  		fs.Visit(func(f *flag.Flag) {
    83  			for _, alias := range aliases {
    84  				if f.Name == alias {
    85  					isSet = true
    86  				}
    87  			}
    88  		})
    89  
    90  		if isSet {
    91  			return true
    92  		}
    93  	}
    94  
    95  	return false
    96  }
    97  
    98  // LocalFlagNames returns a slice of flag names used in this context.
    99  func (cCtx *Context) LocalFlagNames() []string {
   100  	var names []string
   101  	cCtx.flagSet.Visit(makeFlagNameVisitor(&names))
   102  	// Check the flags which have been set via env or file
   103  	if cCtx.Command != nil && cCtx.Command.Flags != nil {
   104  		for _, f := range cCtx.Command.Flags {
   105  			if f.IsSet() {
   106  				names = append(names, f.Names()...)
   107  			}
   108  		}
   109  	}
   110  
   111  	// Sort out the duplicates since flag could be set via multiple
   112  	// paths
   113  	m := map[string]struct{}{}
   114  	var unames []string
   115  	for _, name := range names {
   116  		if _, ok := m[name]; !ok {
   117  			m[name] = struct{}{}
   118  			unames = append(unames, name)
   119  		}
   120  	}
   121  
   122  	return unames
   123  }
   124  
   125  // FlagNames returns a slice of flag names used by the this context and all of
   126  // its parent contexts.
   127  func (cCtx *Context) FlagNames() []string {
   128  	var names []string
   129  	for _, pCtx := range cCtx.Lineage() {
   130  		names = append(names, pCtx.LocalFlagNames()...)
   131  	}
   132  	return names
   133  }
   134  
   135  // Lineage returns *this* context and all of its ancestor contexts in order from
   136  // child to parent
   137  func (cCtx *Context) Lineage() []*Context {
   138  	var lineage []*Context
   139  
   140  	for cur := cCtx; cur != nil; cur = cur.parentContext {
   141  		lineage = append(lineage, cur)
   142  	}
   143  
   144  	return lineage
   145  }
   146  
   147  // Count returns the num of occurrences of this flag
   148  func (cCtx *Context) Count(name string) int {
   149  	if fs := cCtx.lookupFlagSet(name); fs != nil {
   150  		if cf, ok := fs.Lookup(name).Value.(Countable); ok {
   151  			return cf.Count()
   152  		}
   153  	}
   154  	return 0
   155  }
   156  
   157  // Value returns the value of the flag corresponding to `name`
   158  func (cCtx *Context) Value(name string) interface{} {
   159  	if fs := cCtx.lookupFlagSet(name); fs != nil {
   160  		return fs.Lookup(name).Value.(flag.Getter).Get()
   161  	}
   162  	return nil
   163  }
   164  
   165  // Args returns the command line arguments associated with the context.
   166  func (cCtx *Context) Args() Args {
   167  	ret := args(cCtx.flagSet.Args())
   168  	return &ret
   169  }
   170  
   171  // NArg returns the number of the command line arguments.
   172  func (cCtx *Context) NArg() int {
   173  	return cCtx.Args().Len()
   174  }
   175  
   176  func (cCtx *Context) lookupFlag(name string) Flag {
   177  	for _, c := range cCtx.Lineage() {
   178  		if c.Command == nil {
   179  			continue
   180  		}
   181  
   182  		for _, f := range c.Command.Flags {
   183  			for _, n := range f.Names() {
   184  				if n == name {
   185  					return f
   186  				}
   187  			}
   188  		}
   189  	}
   190  
   191  	if cCtx.App != nil {
   192  		for _, f := range cCtx.App.Flags {
   193  			for _, n := range f.Names() {
   194  				if n == name {
   195  					return f
   196  				}
   197  			}
   198  		}
   199  	}
   200  
   201  	return nil
   202  }
   203  
   204  func (cCtx *Context) lookupFlagSet(name string) *flag.FlagSet {
   205  	for _, c := range cCtx.Lineage() {
   206  		if c.flagSet == nil {
   207  			continue
   208  		}
   209  		if f := c.flagSet.Lookup(name); f != nil {
   210  			return c.flagSet
   211  		}
   212  	}
   213  	cCtx.onInvalidFlag(name)
   214  	return nil
   215  }
   216  
   217  func (cCtx *Context) checkRequiredFlags(flags []Flag) requiredFlagsErr {
   218  	var missingFlags []string
   219  	for _, f := range flags {
   220  		if rf, ok := f.(RequiredFlag); ok && rf.IsRequired() {
   221  			var flagPresent bool
   222  			var flagName string
   223  
   224  			flagNames := f.Names()
   225  			flagName = flagNames[0]
   226  
   227  			for _, key := range flagNames {
   228  				if cCtx.IsSet(strings.TrimSpace(key)) {
   229  					flagPresent = true
   230  				}
   231  			}
   232  
   233  			if !flagPresent && flagName != "" {
   234  				missingFlags = append(missingFlags, flagName)
   235  			}
   236  		}
   237  	}
   238  
   239  	if len(missingFlags) != 0 {
   240  		return &errRequiredFlags{missingFlags: missingFlags}
   241  	}
   242  
   243  	return nil
   244  }
   245  
   246  func (cCtx *Context) onInvalidFlag(name string) {
   247  	for cCtx != nil {
   248  		if cCtx.App != nil && cCtx.App.InvalidFlagAccessHandler != nil {
   249  			cCtx.App.InvalidFlagAccessHandler(cCtx, name)
   250  			break
   251  		}
   252  		cCtx = cCtx.parentContext
   253  	}
   254  }
   255  
   256  func makeFlagNameVisitor(names *[]string) func(*flag.Flag) {
   257  	return func(f *flag.Flag) {
   258  		nameParts := strings.Split(f.Name, ",")
   259  		name := strings.TrimSpace(nameParts[0])
   260  
   261  		for _, part := range nameParts {
   262  			part = strings.TrimSpace(part)
   263  			if len(part) > len(name) {
   264  				name = part
   265  			}
   266  		}
   267  
   268  		if name != "" {
   269  			*names = append(*names, name)
   270  		}
   271  	}
   272  }
   273  

View as plain text