github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/cli/flag.go (about)

     1  package cli
     2  
     3  import (
     4  	"flag"
     5  	"fmt"
     6  	"os"
     7  	"runtime"
     8  	"strconv"
     9  	"strings"
    10  	"time"
    11  )
    12  
    13  // This flag enables bash-completion for all commands and subcommands
    14  var BashCompletionFlag = BoolFlag{
    15  	Name: "generate-bash-completion",
    16  }
    17  
    18  // This flag prints the version for the application
    19  var VersionFlag = BoolFlag{
    20  	Name:  "version, v",
    21  	Usage: "print the version",
    22  }
    23  
    24  // This flag prints the help for all commands and subcommands
    25  // Set to the zero value (BoolFlag{}) to disable flag -- keeps subcommand
    26  // unless HideHelp is set to true)
    27  var HelpFlag = BoolFlag{
    28  	Name:  "help, h",
    29  	Usage: "show help",
    30  }
    31  
    32  // Flag is a common interface related to parsing flags in cli.
    33  // For more advanced flag parsing techniques, it is recommended that
    34  // this interface be implemented.
    35  type Flag interface {
    36  	fmt.Stringer
    37  	// Apply Flag settings to the given flag set
    38  	Apply(*flag.FlagSet)
    39  	GetName() string
    40  }
    41  
    42  func flagSet(name string, flags []Flag) *flag.FlagSet {
    43  	set := flag.NewFlagSet(name, flag.ContinueOnError)
    44  
    45  	for _, f := range flags {
    46  		f.Apply(set)
    47  	}
    48  	return set
    49  }
    50  
    51  func eachName(longName string, fn func(string)) {
    52  	parts := strings.Split(longName, ",")
    53  	for _, name := range parts {
    54  		name = strings.Trim(name, " ")
    55  		fn(name)
    56  	}
    57  }
    58  
    59  // Generic is a generic parseable type identified by a specific flag
    60  type Generic interface {
    61  	Set(value string) error
    62  	String() string
    63  }
    64  
    65  // GenericFlag is the flag type for types implementing Generic
    66  type GenericFlag struct {
    67  	Name   string
    68  	Value  Generic
    69  	Usage  string
    70  	EnvVar string
    71  }
    72  
    73  // String returns the string representation of the generic flag to display the
    74  // help text to the user (uses the String() method of the generic flag to show
    75  // the value)
    76  func (f GenericFlag) String() string {
    77  	return withEnvHint(f.EnvVar, fmt.Sprintf("%s %v\t%v", prefixedNames(f.Name), f.FormatValueHelp(), f.Usage))
    78  }
    79  
    80  func (f GenericFlag) FormatValueHelp() string {
    81  	if f.Value == nil {
    82  		return ""
    83  	}
    84  	s := f.Value.String()
    85  	if len(s) == 0 {
    86  		return ""
    87  	}
    88  	return fmt.Sprintf("\"%s\"", s)
    89  }
    90  
    91  // Apply takes the flagset and calls Set on the generic flag with the value
    92  // provided by the user for parsing by the flag
    93  func (f GenericFlag) Apply(set *flag.FlagSet) {
    94  	val := f.Value
    95  	if f.EnvVar != "" {
    96  		for _, envVar := range strings.Split(f.EnvVar, ",") {
    97  			envVar = strings.TrimSpace(envVar)
    98  			if envVal := os.Getenv(envVar); envVal != "" {
    99  				val.Set(envVal)
   100  				break
   101  			}
   102  		}
   103  	}
   104  
   105  	eachName(f.Name, func(name string) {
   106  		set.Var(f.Value, name, f.Usage)
   107  	})
   108  }
   109  
   110  func (f GenericFlag) GetName() string {
   111  	return f.Name
   112  }
   113  
   114  // StringSlice is an opaque type for []string to satisfy flag.Value
   115  type StringSlice []string
   116  
   117  // Set appends the string value to the list of values
   118  func (f *StringSlice) Set(value string) error {
   119  	*f = append(*f, value)
   120  	return nil
   121  }
   122  
   123  // String returns a readable representation of this value (for usage defaults)
   124  func (f *StringSlice) String() string {
   125  	return fmt.Sprintf("%s", *f)
   126  }
   127  
   128  // Value returns the slice of strings set by this flag
   129  func (f *StringSlice) Value() []string {
   130  	return *f
   131  }
   132  
   133  // StringSlice is a string flag that can be specified multiple times on the
   134  // command-line
   135  type StringSliceFlag struct {
   136  	Name   string
   137  	Value  *StringSlice
   138  	Usage  string
   139  	EnvVar string
   140  }
   141  
   142  // String returns the usage
   143  func (f StringSliceFlag) String() string {
   144  	firstName := strings.Trim(strings.Split(f.Name, ",")[0], " ")
   145  	pref := prefixFor(firstName)
   146  	return withEnvHint(f.EnvVar, fmt.Sprintf("%s [%v]\t%v", prefixedNames(f.Name), pref+firstName+" option "+pref+firstName+" option", f.Usage))
   147  }
   148  
   149  // Apply populates the flag given the flag set and environment
   150  func (f StringSliceFlag) Apply(set *flag.FlagSet) {
   151  	if f.EnvVar != "" {
   152  		for _, envVar := range strings.Split(f.EnvVar, ",") {
   153  			envVar = strings.TrimSpace(envVar)
   154  			if envVal := os.Getenv(envVar); envVal != "" {
   155  				newVal := &StringSlice{}
   156  				for _, s := range strings.Split(envVal, ",") {
   157  					s = strings.TrimSpace(s)
   158  					newVal.Set(s)
   159  				}
   160  				f.Value = newVal
   161  				break
   162  			}
   163  		}
   164  	}
   165  
   166  	eachName(f.Name, func(name string) {
   167  		if f.Value == nil {
   168  			f.Value = &StringSlice{}
   169  		}
   170  		set.Var(f.Value, name, f.Usage)
   171  	})
   172  }
   173  
   174  func (f StringSliceFlag) GetName() string {
   175  	return f.Name
   176  }
   177  
   178  // StringSlice is an opaque type for []int to satisfy flag.Value
   179  type IntSlice []int
   180  
   181  // Set parses the value into an integer and appends it to the list of values
   182  func (f *IntSlice) Set(value string) error {
   183  	tmp, err := strconv.Atoi(value)
   184  	if err != nil {
   185  		return err
   186  	} else {
   187  		*f = append(*f, tmp)
   188  	}
   189  	return nil
   190  }
   191  
   192  // String returns a readable representation of this value (for usage defaults)
   193  func (f *IntSlice) String() string {
   194  	return fmt.Sprintf("%d", *f)
   195  }
   196  
   197  // Value returns the slice of ints set by this flag
   198  func (f *IntSlice) Value() []int {
   199  	return *f
   200  }
   201  
   202  // IntSliceFlag is an int flag that can be specified multiple times on the
   203  // command-line
   204  type IntSliceFlag struct {
   205  	Name   string
   206  	Value  *IntSlice
   207  	Usage  string
   208  	EnvVar string
   209  }
   210  
   211  // String returns the usage
   212  func (f IntSliceFlag) String() string {
   213  	firstName := strings.Trim(strings.Split(f.Name, ",")[0], " ")
   214  	pref := prefixFor(firstName)
   215  	return withEnvHint(f.EnvVar, fmt.Sprintf("%s [%v]\t%v", prefixedNames(f.Name), pref+firstName+" option "+pref+firstName+" option", f.Usage))
   216  }
   217  
   218  // Apply populates the flag given the flag set and environment
   219  func (f IntSliceFlag) Apply(set *flag.FlagSet) {
   220  	if f.EnvVar != "" {
   221  		for _, envVar := range strings.Split(f.EnvVar, ",") {
   222  			envVar = strings.TrimSpace(envVar)
   223  			if envVal := os.Getenv(envVar); envVal != "" {
   224  				newVal := &IntSlice{}
   225  				for _, s := range strings.Split(envVal, ",") {
   226  					s = strings.TrimSpace(s)
   227  					err := newVal.Set(s)
   228  					if err != nil {
   229  						fmt.Fprintf(os.Stderr, err.Error())
   230  					}
   231  				}
   232  				f.Value = newVal
   233  				break
   234  			}
   235  		}
   236  	}
   237  
   238  	eachName(f.Name, func(name string) {
   239  		if f.Value == nil {
   240  			f.Value = &IntSlice{}
   241  		}
   242  		set.Var(f.Value, name, f.Usage)
   243  	})
   244  }
   245  
   246  func (f IntSliceFlag) GetName() string {
   247  	return f.Name
   248  }
   249  
   250  // BoolFlag is a switch that defaults to false
   251  type BoolFlag struct {
   252  	Name        string
   253  	Usage       string
   254  	EnvVar      string
   255  	Destination *bool
   256  }
   257  
   258  // String returns a readable representation of this value (for usage defaults)
   259  func (f BoolFlag) String() string {
   260  	return withEnvHint(f.EnvVar, fmt.Sprintf("%s\t%v", prefixedNames(f.Name), f.Usage))
   261  }
   262  
   263  // Apply populates the flag given the flag set and environment
   264  func (f BoolFlag) Apply(set *flag.FlagSet) {
   265  	val := false
   266  	if f.EnvVar != "" {
   267  		for _, envVar := range strings.Split(f.EnvVar, ",") {
   268  			envVar = strings.TrimSpace(envVar)
   269  			if envVal := os.Getenv(envVar); envVal != "" {
   270  				envValBool, err := strconv.ParseBool(envVal)
   271  				if err == nil {
   272  					val = envValBool
   273  				}
   274  				break
   275  			}
   276  		}
   277  	}
   278  
   279  	eachName(f.Name, func(name string) {
   280  		if f.Destination != nil {
   281  			set.BoolVar(f.Destination, name, val, f.Usage)
   282  			return
   283  		}
   284  		set.Bool(name, val, f.Usage)
   285  	})
   286  }
   287  
   288  func (f BoolFlag) GetName() string {
   289  	return f.Name
   290  }
   291  
   292  // BoolTFlag this represents a boolean flag that is true by default, but can
   293  // still be set to false by --some-flag=false
   294  type BoolTFlag struct {
   295  	Name        string
   296  	Usage       string
   297  	EnvVar      string
   298  	Destination *bool
   299  }
   300  
   301  // String returns a readable representation of this value (for usage defaults)
   302  func (f BoolTFlag) String() string {
   303  	return withEnvHint(f.EnvVar, fmt.Sprintf("%s\t%v", prefixedNames(f.Name), f.Usage))
   304  }
   305  
   306  // Apply populates the flag given the flag set and environment
   307  func (f BoolTFlag) Apply(set *flag.FlagSet) {
   308  	val := true
   309  	if f.EnvVar != "" {
   310  		for _, envVar := range strings.Split(f.EnvVar, ",") {
   311  			envVar = strings.TrimSpace(envVar)
   312  			if envVal := os.Getenv(envVar); envVal != "" {
   313  				envValBool, err := strconv.ParseBool(envVal)
   314  				if err == nil {
   315  					val = envValBool
   316  					break
   317  				}
   318  			}
   319  		}
   320  	}
   321  
   322  	eachName(f.Name, func(name string) {
   323  		if f.Destination != nil {
   324  			set.BoolVar(f.Destination, name, val, f.Usage)
   325  			return
   326  		}
   327  		set.Bool(name, val, f.Usage)
   328  	})
   329  }
   330  
   331  func (f BoolTFlag) GetName() string {
   332  	return f.Name
   333  }
   334  
   335  // StringFlag represents a flag that takes as string value
   336  type StringFlag struct {
   337  	Name        string
   338  	Value       string
   339  	Usage       string
   340  	EnvVar      string
   341  	Destination *string
   342  }
   343  
   344  // String returns the usage
   345  func (f StringFlag) String() string {
   346  	return withEnvHint(f.EnvVar, fmt.Sprintf("%s %v\t%v", prefixedNames(f.Name), f.FormatValueHelp(), f.Usage))
   347  }
   348  
   349  func (f StringFlag) FormatValueHelp() string {
   350  	s := f.Value
   351  	if len(s) == 0 {
   352  		return ""
   353  	}
   354  	return fmt.Sprintf("\"%s\"", s)
   355  }
   356  
   357  // Apply populates the flag given the flag set and environment
   358  func (f StringFlag) Apply(set *flag.FlagSet) {
   359  	if f.EnvVar != "" {
   360  		for _, envVar := range strings.Split(f.EnvVar, ",") {
   361  			envVar = strings.TrimSpace(envVar)
   362  			if envVal := os.Getenv(envVar); envVal != "" {
   363  				f.Value = envVal
   364  				break
   365  			}
   366  		}
   367  	}
   368  
   369  	eachName(f.Name, func(name string) {
   370  		if f.Destination != nil {
   371  			set.StringVar(f.Destination, name, f.Value, f.Usage)
   372  			return
   373  		}
   374  		set.String(name, f.Value, f.Usage)
   375  	})
   376  }
   377  
   378  func (f StringFlag) GetName() string {
   379  	return f.Name
   380  }
   381  
   382  // IntFlag is a flag that takes an integer
   383  // Errors if the value provided cannot be parsed
   384  type IntFlag struct {
   385  	Name        string
   386  	Value       int
   387  	Usage       string
   388  	EnvVar      string
   389  	Destination *int
   390  }
   391  
   392  // String returns the usage
   393  func (f IntFlag) String() string {
   394  	return withEnvHint(f.EnvVar, fmt.Sprintf("%s \"%v\"\t%v", prefixedNames(f.Name), f.Value, f.Usage))
   395  }
   396  
   397  // Apply populates the flag given the flag set and environment
   398  func (f IntFlag) Apply(set *flag.FlagSet) {
   399  	if f.EnvVar != "" {
   400  		for _, envVar := range strings.Split(f.EnvVar, ",") {
   401  			envVar = strings.TrimSpace(envVar)
   402  			if envVal := os.Getenv(envVar); envVal != "" {
   403  				envValInt, err := strconv.ParseInt(envVal, 0, 64)
   404  				if err == nil {
   405  					f.Value = int(envValInt)
   406  					break
   407  				}
   408  			}
   409  		}
   410  	}
   411  
   412  	eachName(f.Name, func(name string) {
   413  		if f.Destination != nil {
   414  			set.IntVar(f.Destination, name, f.Value, f.Usage)
   415  			return
   416  		}
   417  		set.Int(name, f.Value, f.Usage)
   418  	})
   419  }
   420  
   421  func (f IntFlag) GetName() string {
   422  	return f.Name
   423  }
   424  
   425  // DurationFlag is a flag that takes a duration specified in Go's duration
   426  // format: https://yougam/libraries/pkg/time/#ParseDuration
   427  type DurationFlag struct {
   428  	Name        string
   429  	Value       time.Duration
   430  	Usage       string
   431  	EnvVar      string
   432  	Destination *time.Duration
   433  }
   434  
   435  // String returns a readable representation of this value (for usage defaults)
   436  func (f DurationFlag) String() string {
   437  	return withEnvHint(f.EnvVar, fmt.Sprintf("%s \"%v\"\t%v", prefixedNames(f.Name), f.Value, f.Usage))
   438  }
   439  
   440  // Apply populates the flag given the flag set and environment
   441  func (f DurationFlag) Apply(set *flag.FlagSet) {
   442  	if f.EnvVar != "" {
   443  		for _, envVar := range strings.Split(f.EnvVar, ",") {
   444  			envVar = strings.TrimSpace(envVar)
   445  			if envVal := os.Getenv(envVar); envVal != "" {
   446  				envValDuration, err := time.ParseDuration(envVal)
   447  				if err == nil {
   448  					f.Value = envValDuration
   449  					break
   450  				}
   451  			}
   452  		}
   453  	}
   454  
   455  	eachName(f.Name, func(name string) {
   456  		if f.Destination != nil {
   457  			set.DurationVar(f.Destination, name, f.Value, f.Usage)
   458  			return
   459  		}
   460  		set.Duration(name, f.Value, f.Usage)
   461  	})
   462  }
   463  
   464  func (f DurationFlag) GetName() string {
   465  	return f.Name
   466  }
   467  
   468  // Float64Flag is a flag that takes an float value
   469  // Errors if the value provided cannot be parsed
   470  type Float64Flag struct {
   471  	Name        string
   472  	Value       float64
   473  	Usage       string
   474  	EnvVar      string
   475  	Destination *float64
   476  }
   477  
   478  // String returns the usage
   479  func (f Float64Flag) String() string {
   480  	return withEnvHint(f.EnvVar, fmt.Sprintf("%s \"%v\"\t%v", prefixedNames(f.Name), f.Value, f.Usage))
   481  }
   482  
   483  // Apply populates the flag given the flag set and environment
   484  func (f Float64Flag) Apply(set *flag.FlagSet) {
   485  	if f.EnvVar != "" {
   486  		for _, envVar := range strings.Split(f.EnvVar, ",") {
   487  			envVar = strings.TrimSpace(envVar)
   488  			if envVal := os.Getenv(envVar); envVal != "" {
   489  				envValFloat, err := strconv.ParseFloat(envVal, 10)
   490  				if err == nil {
   491  					f.Value = float64(envValFloat)
   492  				}
   493  			}
   494  		}
   495  	}
   496  
   497  	eachName(f.Name, func(name string) {
   498  		if f.Destination != nil {
   499  			set.Float64Var(f.Destination, name, f.Value, f.Usage)
   500  			return
   501  		}
   502  		set.Float64(name, f.Value, f.Usage)
   503  	})
   504  }
   505  
   506  func (f Float64Flag) GetName() string {
   507  	return f.Name
   508  }
   509  
   510  func prefixFor(name string) (prefix string) {
   511  	if len(name) == 1 {
   512  		prefix = "-"
   513  	} else {
   514  		prefix = "--"
   515  	}
   516  
   517  	return
   518  }
   519  
   520  func prefixedNames(fullName string) (prefixed string) {
   521  	parts := strings.Split(fullName, ",")
   522  	for i, name := range parts {
   523  		name = strings.Trim(name, " ")
   524  		prefixed += prefixFor(name) + name
   525  		if i < len(parts)-1 {
   526  			prefixed += ", "
   527  		}
   528  	}
   529  	return
   530  }
   531  
   532  func withEnvHint(envVar, str string) string {
   533  	envText := ""
   534  	if envVar != "" {
   535  		prefix := "$"
   536  		suffix := ""
   537  		sep := ", $"
   538  		if runtime.GOOS == "windows" {
   539  			prefix = "%"
   540  			suffix = "%"
   541  			sep = "%, %"
   542  		}
   543  		envText = fmt.Sprintf(" [%s%s%s]", prefix, strings.Join(strings.Split(envVar, ","), sep), suffix)
   544  	}
   545  	return str + envText
   546  }