github.com/loggregator/cli@v6.33.1-0.20180224010324-82334f081791+incompatible/cf/flags/flags.go (about)

     1  package flags
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"strconv"
     7  	"strings"
     8  )
     9  
    10  type FlagSet interface {
    11  	fmt.Stringer
    12  	GetName() string
    13  	GetShortName() string
    14  	GetValue() interface{}
    15  	Set(string)
    16  	Visible() bool
    17  }
    18  
    19  type FlagContext interface {
    20  	Parse(...string) error
    21  	Args() []string
    22  	Int(string) int
    23  	Float64(string) float64
    24  	Bool(string) bool
    25  	String(string) string
    26  	StringSlice(string) []string
    27  	IsSet(string) bool
    28  	SkipFlagParsing(bool)
    29  	NewStringFlag(name string, shortName string, usage string)
    30  	NewStringFlagWithDefault(name string, shortName string, usage string, value string)
    31  	NewBoolFlag(name string, shortName string, usage string)
    32  	NewIntFlag(name string, shortName string, usage string)
    33  	NewIntFlagWithDefault(name string, shortName string, usage string, value int)
    34  	NewFloat64Flag(name string, shortName string, usage string)
    35  	NewFloat64FlagWithDefault(name string, shortName string, usage string, value float64)
    36  	NewStringSliceFlag(name string, shortName string, usage string)
    37  	NewStringSliceFlagWithDefault(name string, shortName string, usage string, value []string)
    38  	ShowUsage(leadingSpace int) string
    39  }
    40  
    41  type flagContext struct {
    42  	flagsets        map[string]FlagSet
    43  	args            []string
    44  	cmdFlags        map[string]FlagSet //valid flags for command
    45  	cursor          int
    46  	skipFlagParsing bool
    47  }
    48  
    49  func New() FlagContext {
    50  	return &flagContext{
    51  		flagsets: make(map[string]FlagSet),
    52  		cmdFlags: make(map[string]FlagSet),
    53  		cursor:   0,
    54  	}
    55  }
    56  
    57  func NewFlagContext(cmdFlags map[string]FlagSet) FlagContext {
    58  	return &flagContext{
    59  		flagsets: make(map[string]FlagSet),
    60  		cmdFlags: cmdFlags,
    61  		cursor:   0,
    62  	}
    63  }
    64  
    65  func (c *flagContext) Parse(args ...string) error {
    66  	c.setDefaultFlagValueIfAny()
    67  
    68  	for c.cursor <= len(args)-1 {
    69  		arg := args[c.cursor]
    70  
    71  		if !c.skipFlagParsing && (strings.HasPrefix(arg, "-") || strings.HasPrefix(arg, "--")) {
    72  			flg := strings.TrimLeft(strings.TrimLeft(arg, "-"), "-")
    73  
    74  			c.extractEqualSignIfAny(&flg, &args)
    75  
    76  			flagset, ok := c.cmdFlags[flg]
    77  			if !ok {
    78  				flg = c.getFlagNameWithShortName(flg)
    79  				if flagset, ok = c.cmdFlags[flg]; !ok {
    80  					return errors.New("Invalid flag: " + arg)
    81  				}
    82  			}
    83  
    84  			switch flagset.GetValue().(type) {
    85  			case bool:
    86  				c.flagsets[flg] = &BoolFlag{Name: flg, Value: c.getBoolFlagValue(args)}
    87  			case int:
    88  				v, err := c.getFlagValue(args)
    89  				if err != nil {
    90  					return err
    91  				}
    92  				i, err := strconv.ParseInt(v, 10, 32)
    93  				if err != nil {
    94  					return errors.New("Value for flag '" + flg + "' must be an integer")
    95  				}
    96  				c.flagsets[flg] = &IntFlag{Name: flg, Value: int(i)}
    97  			case float64:
    98  				v, err := c.getFlagValue(args)
    99  				if err != nil {
   100  					return err
   101  				}
   102  				i, err := strconv.ParseFloat(v, 64)
   103  				if err != nil {
   104  					return errors.New("Value for flag '" + flg + "' must be a float64")
   105  				}
   106  				c.flagsets[flg] = &Float64Flag{Name: flg, Value: float64(i)}
   107  			case string:
   108  				v, err := c.getFlagValue(args)
   109  				if err != nil {
   110  					return err
   111  				}
   112  				c.flagsets[flg] = &StringFlag{Name: flg, Value: v}
   113  			case []string:
   114  				v, err := c.getFlagValue(args)
   115  				if err != nil {
   116  					return err
   117  				}
   118  				if _, ok = c.flagsets[flg]; !ok {
   119  					c.flagsets[flg] = &StringSliceFlag{Name: flg, Value: []string{v}}
   120  				} else {
   121  					c.flagsets[flg].Set(v)
   122  				}
   123  			case backwardsCompatibilityType:
   124  				// do nothing
   125  			}
   126  		} else {
   127  			c.args = append(c.args, args[c.cursor])
   128  		}
   129  		c.cursor++
   130  	}
   131  	return nil
   132  }
   133  
   134  func (c *flagContext) getFlagValue(args []string) (string, error) {
   135  	if c.cursor >= len(args)-1 {
   136  		return "", errors.New("No value provided for flag: " + args[c.cursor])
   137  	}
   138  
   139  	c.cursor++
   140  	return args[c.cursor], nil
   141  }
   142  
   143  func (c *flagContext) getBoolFlagValue(args []string) bool {
   144  	if c.cursor >= len(args)-1 {
   145  		return true
   146  	}
   147  
   148  	b, err := strconv.ParseBool(args[c.cursor+1])
   149  	if err == nil {
   150  		c.cursor++
   151  		return b
   152  	}
   153  	return true
   154  }
   155  
   156  func (c *flagContext) Args() []string {
   157  	return c.args
   158  }
   159  
   160  func (c *flagContext) IsSet(k string) bool {
   161  	return c.isFlagProvided(&k)
   162  }
   163  
   164  func (c *flagContext) Int(k string) int {
   165  	if !c.isFlagProvided(&k) {
   166  		return 0
   167  	}
   168  
   169  	v := c.flagsets[k].GetValue()
   170  	switch v.(type) {
   171  	case int:
   172  		return v.(int)
   173  	}
   174  
   175  	return 0
   176  }
   177  
   178  func (c *flagContext) Float64(k string) float64 {
   179  	if !c.isFlagProvided(&k) {
   180  		return 0
   181  	}
   182  
   183  	v := c.flagsets[k].GetValue()
   184  	switch v.(type) {
   185  	case float64:
   186  		return v.(float64)
   187  	}
   188  	return 0
   189  }
   190  
   191  func (c *flagContext) String(k string) string {
   192  	if !c.isFlagProvided(&k) {
   193  		return ""
   194  	}
   195  
   196  	v := c.flagsets[k].GetValue()
   197  	switch v.(type) {
   198  	case string:
   199  		return v.(string)
   200  	}
   201  	return ""
   202  }
   203  
   204  func (c *flagContext) Bool(k string) bool {
   205  	if !c.isFlagProvided(&k) {
   206  		return false
   207  	}
   208  
   209  	v := c.flagsets[k].GetValue()
   210  	switch v.(type) {
   211  	case bool:
   212  		return v.(bool)
   213  	}
   214  
   215  	return false
   216  }
   217  
   218  func (c *flagContext) StringSlice(k string) []string {
   219  	if !c.isFlagProvided(&k) {
   220  		return []string{}
   221  	}
   222  
   223  	v := c.flagsets[k].GetValue()
   224  	switch v.(type) {
   225  	case []string:
   226  		return v.([]string)
   227  	}
   228  	return []string{}
   229  }
   230  
   231  func (c *flagContext) SkipFlagParsing(skip bool) {
   232  	c.skipFlagParsing = skip
   233  }
   234  
   235  func (c *flagContext) extractEqualSignIfAny(flg *string, args *[]string) {
   236  	if strings.Contains(*flg, "=") {
   237  		tmpAry := strings.SplitN(*flg, "=", 2)
   238  		*flg = tmpAry[0]
   239  		tmpArg := append((*args)[:c.cursor], tmpAry[1])
   240  		*args = append(tmpArg, (*args)[c.cursor:]...)
   241  	}
   242  }
   243  
   244  func (c *flagContext) setDefaultFlagValueIfAny() {
   245  	var v interface{}
   246  
   247  	for flgName, flg := range c.cmdFlags {
   248  		v = flg.GetValue()
   249  		switch v.(type) {
   250  		case bool:
   251  			if v.(bool) != false {
   252  				c.flagsets[flgName] = &BoolFlag{Name: flgName, Value: v.(bool)}
   253  			}
   254  		case int:
   255  			if v.(int) != 0 {
   256  				c.flagsets[flgName] = &IntFlag{Name: flgName, Value: v.(int)}
   257  			}
   258  		case float64:
   259  			if v.(float64) != 0 {
   260  				c.flagsets[flgName] = &Float64Flag{Name: flgName, Value: v.(float64)}
   261  			}
   262  		case string:
   263  			if len(v.(string)) != 0 {
   264  				c.flagsets[flgName] = &StringFlag{Name: flgName, Value: v.(string)}
   265  			}
   266  		case []string:
   267  			if len(v.([]string)) != 0 {
   268  				c.flagsets[flgName] = &StringSliceFlag{Name: flgName, Value: v.([]string)}
   269  			}
   270  		}
   271  	}
   272  
   273  }
   274  
   275  func (c *flagContext) getFlagNameWithShortName(shortName string) string {
   276  	for n, f := range c.cmdFlags {
   277  		if f.GetShortName() == shortName {
   278  			return n
   279  		}
   280  	}
   281  	return ""
   282  }
   283  
   284  func (c *flagContext) isFlagProvided(flg *string) bool {
   285  	if _, ok := c.flagsets[*flg]; !ok {
   286  		*flg = c.getFlagNameWithShortName(*flg)
   287  		if _, ok := c.flagsets[*flg]; !ok {
   288  			return false
   289  		}
   290  	}
   291  
   292  	return true
   293  }