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

     1  package altsrc
     2  
     3  import (
     4  	"flag"
     5  	"fmt"
     6  	"os"
     7  	"strconv"
     8  	"strings"
     9  
    10  	"github.com/insionng/yougam/libraries/cli"
    11  )
    12  
    13  // FlagInputSourceExtension is an extension interface of cli.Flag that
    14  // allows a value to be set on the existing parsed flags.
    15  type FlagInputSourceExtension interface {
    16  	cli.Flag
    17  	ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error
    18  }
    19  
    20  // ApplyInputSourceValues iterates over all provided flags and
    21  // executes ApplyInputSourceValue on flags implementing the
    22  // FlagInputSourceExtension interface to initialize these flags
    23  // to an alternate input source.
    24  func ApplyInputSourceValues(context *cli.Context, inputSourceContext InputSourceContext, flags []cli.Flag) error {
    25  	for _, f := range flags {
    26  		inputSourceExtendedFlag, isType := f.(FlagInputSourceExtension)
    27  		if isType {
    28  			err := inputSourceExtendedFlag.ApplyInputSourceValue(context, inputSourceContext)
    29  			if err != nil {
    30  				return err
    31  			}
    32  		}
    33  	}
    34  
    35  	return nil
    36  }
    37  
    38  // InitInputSource is used to to setup an InputSourceContext on a cli.Command Before method. It will create a new
    39  // input source based on the func provided. If there is no error it will then apply the new input source to any flags
    40  // that are supported by the input source
    41  func InitInputSource(flags []cli.Flag, createInputSource func() (InputSourceContext, error)) func(context *cli.Context) error {
    42  	return func(context *cli.Context) error {
    43  		inputSource, err := createInputSource()
    44  		if err != nil {
    45  			return fmt.Errorf("Unable to create input source: inner error: \n'%v'", err.Error())
    46  		}
    47  
    48  		return ApplyInputSourceValues(context, inputSource, flags)
    49  	}
    50  }
    51  
    52  // InitInputSourceWithContext is used to to setup an InputSourceContext on a cli.Command Before method. It will create a new
    53  // input source based on the func provided with potentially using existing cli.Context values to initialize itself. If there is
    54  // no error it will then apply the new input source to any flags that are supported by the input source
    55  func InitInputSourceWithContext(flags []cli.Flag, createInputSource func(context *cli.Context) (InputSourceContext, error)) func(context *cli.Context) error {
    56  	return func(context *cli.Context) error {
    57  		inputSource, err := createInputSource(context)
    58  		if err != nil {
    59  			return fmt.Errorf("Unable to create input source with context: inner error: \n'%v'", err.Error())
    60  		}
    61  
    62  		return ApplyInputSourceValues(context, inputSource, flags)
    63  	}
    64  }
    65  
    66  // GenericFlag is the flag type that wraps cli.GenericFlag to allow
    67  // for other values to be specified
    68  type GenericFlag struct {
    69  	cli.GenericFlag
    70  	set *flag.FlagSet
    71  }
    72  
    73  // NewGenericFlag creates a new GenericFlag
    74  func NewGenericFlag(flag cli.GenericFlag) *GenericFlag {
    75  	return &GenericFlag{GenericFlag: flag, set: nil}
    76  }
    77  
    78  // ApplyInputSourceValue applies a generic value to the flagSet if required
    79  func (f *GenericFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error {
    80  	if f.set != nil {
    81  		if !context.IsSet(f.Name) && !isEnvVarSet(f.EnvVar) {
    82  			value, err := isc.Generic(f.GenericFlag.Name)
    83  			if err != nil {
    84  				return err
    85  			}
    86  			if value != nil {
    87  				eachName(f.Name, func(name string) {
    88  					f.set.Set(f.Name, value.String())
    89  				})
    90  			}
    91  		}
    92  	}
    93  
    94  	return nil
    95  }
    96  
    97  // Apply saves the flagSet for later usage then calls
    98  // the wrapped GenericFlag.Apply
    99  func (f *GenericFlag) Apply(set *flag.FlagSet) {
   100  	f.set = set
   101  	f.GenericFlag.Apply(set)
   102  }
   103  
   104  // StringSliceFlag is the flag type that wraps cli.StringSliceFlag to allow
   105  // for other values to be specified
   106  type StringSliceFlag struct {
   107  	cli.StringSliceFlag
   108  	set *flag.FlagSet
   109  }
   110  
   111  // NewStringSliceFlag creates a new StringSliceFlag
   112  func NewStringSliceFlag(flag cli.StringSliceFlag) *StringSliceFlag {
   113  	return &StringSliceFlag{StringSliceFlag: flag, set: nil}
   114  }
   115  
   116  // ApplyInputSourceValue applies a StringSlice value to the flagSet if required
   117  func (f *StringSliceFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error {
   118  	if f.set != nil {
   119  		if !context.IsSet(f.Name) && !isEnvVarSet(f.EnvVar) {
   120  			value, err := isc.StringSlice(f.StringSliceFlag.Name)
   121  			if err != nil {
   122  				return err
   123  			}
   124  			if value != nil {
   125  				var sliceValue cli.StringSlice = value
   126  				eachName(f.Name, func(name string) {
   127  					underlyingFlag := f.set.Lookup(f.Name)
   128  					if underlyingFlag != nil {
   129  						underlyingFlag.Value = &sliceValue
   130  					}
   131  				})
   132  			}
   133  		}
   134  	}
   135  	return nil
   136  }
   137  
   138  // Apply saves the flagSet for later usage then calls
   139  // the wrapped StringSliceFlag.Apply
   140  func (f *StringSliceFlag) Apply(set *flag.FlagSet) {
   141  	f.set = set
   142  	f.StringSliceFlag.Apply(set)
   143  }
   144  
   145  // IntSliceFlag is the flag type that wraps cli.IntSliceFlag to allow
   146  // for other values to be specified
   147  type IntSliceFlag struct {
   148  	cli.IntSliceFlag
   149  	set *flag.FlagSet
   150  }
   151  
   152  // NewIntSliceFlag creates a new IntSliceFlag
   153  func NewIntSliceFlag(flag cli.IntSliceFlag) *IntSliceFlag {
   154  	return &IntSliceFlag{IntSliceFlag: flag, set: nil}
   155  }
   156  
   157  // ApplyInputSourceValue applies a IntSlice value if required
   158  func (f *IntSliceFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error {
   159  	if f.set != nil {
   160  		if !context.IsSet(f.Name) && !isEnvVarSet(f.EnvVar) {
   161  			value, err := isc.IntSlice(f.IntSliceFlag.Name)
   162  			if err != nil {
   163  				return err
   164  			}
   165  			if value != nil {
   166  				var sliceValue cli.IntSlice = value
   167  				eachName(f.Name, func(name string) {
   168  					underlyingFlag := f.set.Lookup(f.Name)
   169  					if underlyingFlag != nil {
   170  						underlyingFlag.Value = &sliceValue
   171  					}
   172  				})
   173  			}
   174  		}
   175  	}
   176  	return nil
   177  }
   178  
   179  // Apply saves the flagSet for later usage then calls
   180  // the wrapped IntSliceFlag.Apply
   181  func (f *IntSliceFlag) Apply(set *flag.FlagSet) {
   182  	f.set = set
   183  	f.IntSliceFlag.Apply(set)
   184  }
   185  
   186  // BoolFlag is the flag type that wraps cli.BoolFlag to allow
   187  // for other values to be specified
   188  type BoolFlag struct {
   189  	cli.BoolFlag
   190  	set *flag.FlagSet
   191  }
   192  
   193  // NewBoolFlag creates a new BoolFlag
   194  func NewBoolFlag(flag cli.BoolFlag) *BoolFlag {
   195  	return &BoolFlag{BoolFlag: flag, set: nil}
   196  }
   197  
   198  // ApplyInputSourceValue applies a Bool value to the flagSet if required
   199  func (f *BoolFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error {
   200  	if f.set != nil {
   201  		if !context.IsSet(f.Name) && !isEnvVarSet(f.EnvVar) {
   202  			value, err := isc.Bool(f.BoolFlag.Name)
   203  			if err != nil {
   204  				return err
   205  			}
   206  			if value {
   207  				eachName(f.Name, func(name string) {
   208  					f.set.Set(f.Name, strconv.FormatBool(value))
   209  				})
   210  			}
   211  		}
   212  	}
   213  	return nil
   214  }
   215  
   216  // Apply saves the flagSet for later usage then calls
   217  // the wrapped BoolFlag.Apply
   218  func (f *BoolFlag) Apply(set *flag.FlagSet) {
   219  	f.set = set
   220  	f.BoolFlag.Apply(set)
   221  }
   222  
   223  // BoolTFlag is the flag type that wraps cli.BoolTFlag to allow
   224  // for other values to be specified
   225  type BoolTFlag struct {
   226  	cli.BoolTFlag
   227  	set *flag.FlagSet
   228  }
   229  
   230  // NewBoolTFlag creates a new BoolTFlag
   231  func NewBoolTFlag(flag cli.BoolTFlag) *BoolTFlag {
   232  	return &BoolTFlag{BoolTFlag: flag, set: nil}
   233  }
   234  
   235  // ApplyInputSourceValue applies a BoolT value to the flagSet if required
   236  func (f *BoolTFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error {
   237  	if f.set != nil {
   238  		if !context.IsSet(f.Name) && !isEnvVarSet(f.EnvVar) {
   239  			value, err := isc.BoolT(f.BoolTFlag.Name)
   240  			if err != nil {
   241  				return err
   242  			}
   243  			if !value {
   244  				eachName(f.Name, func(name string) {
   245  					f.set.Set(f.Name, strconv.FormatBool(value))
   246  				})
   247  			}
   248  		}
   249  	}
   250  	return nil
   251  }
   252  
   253  // Apply saves the flagSet for later usage then calls
   254  // the wrapped BoolTFlag.Apply
   255  func (f *BoolTFlag) Apply(set *flag.FlagSet) {
   256  	f.set = set
   257  
   258  	f.BoolTFlag.Apply(set)
   259  }
   260  
   261  // StringFlag is the flag type that wraps cli.StringFlag to allow
   262  // for other values to be specified
   263  type StringFlag struct {
   264  	cli.StringFlag
   265  	set *flag.FlagSet
   266  }
   267  
   268  // NewStringFlag creates a new StringFlag
   269  func NewStringFlag(flag cli.StringFlag) *StringFlag {
   270  	return &StringFlag{StringFlag: flag, set: nil}
   271  }
   272  
   273  // ApplyInputSourceValue applies a String value to the flagSet if required
   274  func (f *StringFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error {
   275  	if f.set != nil {
   276  		if !(context.IsSet(f.Name) || isEnvVarSet(f.EnvVar)) {
   277  			value, err := isc.String(f.StringFlag.Name)
   278  			if err != nil {
   279  				return err
   280  			}
   281  			if value != "" {
   282  				eachName(f.Name, func(name string) {
   283  					f.set.Set(f.Name, value)
   284  				})
   285  			}
   286  		}
   287  	}
   288  	return nil
   289  }
   290  
   291  // Apply saves the flagSet for later usage then calls
   292  // the wrapped StringFlag.Apply
   293  func (f *StringFlag) Apply(set *flag.FlagSet) {
   294  	f.set = set
   295  
   296  	f.StringFlag.Apply(set)
   297  }
   298  
   299  // IntFlag is the flag type that wraps cli.IntFlag to allow
   300  // for other values to be specified
   301  type IntFlag struct {
   302  	cli.IntFlag
   303  	set *flag.FlagSet
   304  }
   305  
   306  // NewIntFlag creates a new IntFlag
   307  func NewIntFlag(flag cli.IntFlag) *IntFlag {
   308  	return &IntFlag{IntFlag: flag, set: nil}
   309  }
   310  
   311  // ApplyInputSourceValue applies a int value to the flagSet if required
   312  func (f *IntFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error {
   313  	if f.set != nil {
   314  		if !(context.IsSet(f.Name) || isEnvVarSet(f.EnvVar)) {
   315  			value, err := isc.Int(f.IntFlag.Name)
   316  			if err != nil {
   317  				return err
   318  			}
   319  			if value > 0 {
   320  				eachName(f.Name, func(name string) {
   321  					f.set.Set(f.Name, strconv.FormatInt(int64(value), 10))
   322  				})
   323  			}
   324  		}
   325  	}
   326  	return nil
   327  }
   328  
   329  // Apply saves the flagSet for later usage then calls
   330  // the wrapped IntFlag.Apply
   331  func (f *IntFlag) Apply(set *flag.FlagSet) {
   332  	f.set = set
   333  	f.IntFlag.Apply(set)
   334  }
   335  
   336  // DurationFlag is the flag type that wraps cli.DurationFlag to allow
   337  // for other values to be specified
   338  type DurationFlag struct {
   339  	cli.DurationFlag
   340  	set *flag.FlagSet
   341  }
   342  
   343  // NewDurationFlag creates a new DurationFlag
   344  func NewDurationFlag(flag cli.DurationFlag) *DurationFlag {
   345  	return &DurationFlag{DurationFlag: flag, set: nil}
   346  }
   347  
   348  // ApplyInputSourceValue applies a Duration value to the flagSet if required
   349  func (f *DurationFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error {
   350  	if f.set != nil {
   351  		if !(context.IsSet(f.Name) || isEnvVarSet(f.EnvVar)) {
   352  			value, err := isc.Duration(f.DurationFlag.Name)
   353  			if err != nil {
   354  				return err
   355  			}
   356  			if value > 0 {
   357  				eachName(f.Name, func(name string) {
   358  					f.set.Set(f.Name, value.String())
   359  				})
   360  			}
   361  		}
   362  	}
   363  	return nil
   364  }
   365  
   366  // Apply saves the flagSet for later usage then calls
   367  // the wrapped DurationFlag.Apply
   368  func (f *DurationFlag) Apply(set *flag.FlagSet) {
   369  	f.set = set
   370  
   371  	f.DurationFlag.Apply(set)
   372  }
   373  
   374  // Float64Flag is the flag type that wraps cli.Float64Flag to allow
   375  // for other values to be specified
   376  type Float64Flag struct {
   377  	cli.Float64Flag
   378  	set *flag.FlagSet
   379  }
   380  
   381  // NewFloat64Flag creates a new Float64Flag
   382  func NewFloat64Flag(flag cli.Float64Flag) *Float64Flag {
   383  	return &Float64Flag{Float64Flag: flag, set: nil}
   384  }
   385  
   386  // ApplyInputSourceValue applies a Float64 value to the flagSet if required
   387  func (f *Float64Flag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error {
   388  	if f.set != nil {
   389  		if !(context.IsSet(f.Name) || isEnvVarSet(f.EnvVar)) {
   390  			value, err := isc.Float64(f.Float64Flag.Name)
   391  			if err != nil {
   392  				return err
   393  			}
   394  			if value > 0 {
   395  				floatStr := float64ToString(value)
   396  				eachName(f.Name, func(name string) {
   397  					f.set.Set(f.Name, floatStr)
   398  				})
   399  			}
   400  		}
   401  	}
   402  	return nil
   403  }
   404  
   405  // Apply saves the flagSet for later usage then calls
   406  // the wrapped Float64Flag.Apply
   407  func (f *Float64Flag) Apply(set *flag.FlagSet) {
   408  	f.set = set
   409  
   410  	f.Float64Flag.Apply(set)
   411  }
   412  
   413  func isEnvVarSet(envVars string) bool {
   414  	for _, envVar := range strings.Split(envVars, ",") {
   415  		envVar = strings.TrimSpace(envVar)
   416  		if envVal := os.Getenv(envVar); envVal != "" {
   417  			// TODO: Can't use this for bools as
   418  			// set means that it was true or false based on
   419  			// Bool flag type, should work for other types
   420  			if len(envVal) > 0 {
   421  				return true
   422  			}
   423  		}
   424  	}
   425  
   426  	return false
   427  }
   428  
   429  func float64ToString(f float64) string {
   430  	return fmt.Sprintf("%v", f)
   431  }
   432  
   433  func eachName(longName string, fn func(string)) {
   434  	parts := strings.Split(longName, ",")
   435  	for _, name := range parts {
   436  		name = strings.Trim(name, " ")
   437  		fn(name)
   438  	}
   439  }