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 }