github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/cli/context.go (about) 1 package cli 2 3 import ( 4 "errors" 5 "flag" 6 "strconv" 7 "strings" 8 "time" 9 ) 10 11 // Context is a type that is passed through to 12 // each Handler action in a cli application. Context 13 // can be used to retrieve context-specific Args and 14 // parsed command-line options. 15 type Context struct { 16 App *App 17 Command Command 18 flagSet *flag.FlagSet 19 setFlags map[string]bool 20 globalSetFlags map[string]bool 21 parentContext *Context 22 } 23 24 // Creates a new context. For use in when invoking an App or Command action. 25 func NewContext(app *App, set *flag.FlagSet, parentCtx *Context) *Context { 26 return &Context{App: app, flagSet: set, parentContext: parentCtx} 27 } 28 29 // Looks up the value of a local int flag, returns 0 if no int flag exists 30 func (c *Context) Int(name string) int { 31 return lookupInt(name, c.flagSet) 32 } 33 34 // Looks up the value of a local time.Duration flag, returns 0 if no time.Duration flag exists 35 func (c *Context) Duration(name string) time.Duration { 36 return lookupDuration(name, c.flagSet) 37 } 38 39 // Looks up the value of a local float64 flag, returns 0 if no float64 flag exists 40 func (c *Context) Float64(name string) float64 { 41 return lookupFloat64(name, c.flagSet) 42 } 43 44 // Looks up the value of a local bool flag, returns false if no bool flag exists 45 func (c *Context) Bool(name string) bool { 46 return lookupBool(name, c.flagSet) 47 } 48 49 // Looks up the value of a local boolT flag, returns false if no bool flag exists 50 func (c *Context) BoolT(name string) bool { 51 return lookupBoolT(name, c.flagSet) 52 } 53 54 // Looks up the value of a local string flag, returns "" if no string flag exists 55 func (c *Context) String(name string) string { 56 return lookupString(name, c.flagSet) 57 } 58 59 // Looks up the value of a local string slice flag, returns nil if no string slice flag exists 60 func (c *Context) StringSlice(name string) []string { 61 return lookupStringSlice(name, c.flagSet) 62 } 63 64 // Looks up the value of a local int slice flag, returns nil if no int slice flag exists 65 func (c *Context) IntSlice(name string) []int { 66 return lookupIntSlice(name, c.flagSet) 67 } 68 69 // Looks up the value of a local generic flag, returns nil if no generic flag exists 70 func (c *Context) Generic(name string) interface{} { 71 return lookupGeneric(name, c.flagSet) 72 } 73 74 // Looks up the value of a global int flag, returns 0 if no int flag exists 75 func (c *Context) GlobalInt(name string) int { 76 if fs := lookupGlobalFlagSet(name, c); fs != nil { 77 return lookupInt(name, fs) 78 } 79 return 0 80 } 81 82 // Looks up the value of a global time.Duration flag, returns 0 if no time.Duration flag exists 83 func (c *Context) GlobalDuration(name string) time.Duration { 84 if fs := lookupGlobalFlagSet(name, c); fs != nil { 85 return lookupDuration(name, fs) 86 } 87 return 0 88 } 89 90 // Looks up the value of a global bool flag, returns false if no bool flag exists 91 func (c *Context) GlobalBool(name string) bool { 92 if fs := lookupGlobalFlagSet(name, c); fs != nil { 93 return lookupBool(name, fs) 94 } 95 return false 96 } 97 98 // Looks up the value of a global string flag, returns "" if no string flag exists 99 func (c *Context) GlobalString(name string) string { 100 if fs := lookupGlobalFlagSet(name, c); fs != nil { 101 return lookupString(name, fs) 102 } 103 return "" 104 } 105 106 // Looks up the value of a global string slice flag, returns nil if no string slice flag exists 107 func (c *Context) GlobalStringSlice(name string) []string { 108 if fs := lookupGlobalFlagSet(name, c); fs != nil { 109 return lookupStringSlice(name, fs) 110 } 111 return nil 112 } 113 114 // Looks up the value of a global int slice flag, returns nil if no int slice flag exists 115 func (c *Context) GlobalIntSlice(name string) []int { 116 if fs := lookupGlobalFlagSet(name, c); fs != nil { 117 return lookupIntSlice(name, fs) 118 } 119 return nil 120 } 121 122 // Looks up the value of a global generic flag, returns nil if no generic flag exists 123 func (c *Context) GlobalGeneric(name string) interface{} { 124 if fs := lookupGlobalFlagSet(name, c); fs != nil { 125 return lookupGeneric(name, fs) 126 } 127 return nil 128 } 129 130 // Returns the number of flags set 131 func (c *Context) NumFlags() int { 132 return c.flagSet.NFlag() 133 } 134 135 // Determines if the flag was actually set 136 func (c *Context) IsSet(name string) bool { 137 if c.setFlags == nil { 138 c.setFlags = make(map[string]bool) 139 c.flagSet.Visit(func(f *flag.Flag) { 140 c.setFlags[f.Name] = true 141 }) 142 } 143 return c.setFlags[name] == true 144 } 145 146 // Determines if the global flag was actually set 147 func (c *Context) GlobalIsSet(name string) bool { 148 if c.globalSetFlags == nil { 149 c.globalSetFlags = make(map[string]bool) 150 ctx := c 151 if ctx.parentContext != nil { 152 ctx = ctx.parentContext 153 } 154 for ; ctx != nil && c.globalSetFlags[name] == false; ctx = ctx.parentContext { 155 ctx.flagSet.Visit(func(f *flag.Flag) { 156 c.globalSetFlags[f.Name] = true 157 }) 158 } 159 } 160 return c.globalSetFlags[name] 161 } 162 163 // Returns a slice of flag names used in this context. 164 func (c *Context) FlagNames() (names []string) { 165 for _, flag := range c.Command.Flags { 166 name := strings.Split(flag.GetName(), ",")[0] 167 if name == "help" { 168 continue 169 } 170 names = append(names, name) 171 } 172 return 173 } 174 175 // Returns a slice of global flag names used by the app. 176 func (c *Context) GlobalFlagNames() (names []string) { 177 for _, flag := range c.App.Flags { 178 name := strings.Split(flag.GetName(), ",")[0] 179 if name == "help" || name == "version" { 180 continue 181 } 182 names = append(names, name) 183 } 184 return 185 } 186 187 // Returns the parent context, if any 188 func (c *Context) Parent() *Context { 189 return c.parentContext 190 } 191 192 type Args []string 193 194 // Returns the command line arguments associated with the context. 195 func (c *Context) Args() Args { 196 args := Args(c.flagSet.Args()) 197 return args 198 } 199 200 // Returns the number of the command line arguments. 201 func (c *Context) NArg() int { 202 return len(c.Args()) 203 } 204 205 // Returns the nth argument, or else a blank string 206 func (a Args) Get(n int) string { 207 if len(a) > n { 208 return a[n] 209 } 210 return "" 211 } 212 213 // Returns the first argument, or else a blank string 214 func (a Args) First() string { 215 return a.Get(0) 216 } 217 218 // Return the rest of the arguments (not the first one) 219 // or else an empty string slice 220 func (a Args) Tail() []string { 221 if len(a) >= 2 { 222 return []string(a)[1:] 223 } 224 return []string{} 225 } 226 227 // Checks if there are any arguments present 228 func (a Args) Present() bool { 229 return len(a) != 0 230 } 231 232 // Swaps arguments at the given indexes 233 func (a Args) Swap(from, to int) error { 234 if from >= len(a) || to >= len(a) { 235 return errors.New("index out of range") 236 } 237 a[from], a[to] = a[to], a[from] 238 return nil 239 } 240 241 func lookupGlobalFlagSet(name string, ctx *Context) *flag.FlagSet { 242 if ctx.parentContext != nil { 243 ctx = ctx.parentContext 244 } 245 for ; ctx != nil; ctx = ctx.parentContext { 246 if f := ctx.flagSet.Lookup(name); f != nil { 247 return ctx.flagSet 248 } 249 } 250 return nil 251 } 252 253 func lookupInt(name string, set *flag.FlagSet) int { 254 f := set.Lookup(name) 255 if f != nil { 256 val, err := strconv.Atoi(f.Value.String()) 257 if err != nil { 258 return 0 259 } 260 return val 261 } 262 263 return 0 264 } 265 266 func lookupDuration(name string, set *flag.FlagSet) time.Duration { 267 f := set.Lookup(name) 268 if f != nil { 269 val, err := time.ParseDuration(f.Value.String()) 270 if err == nil { 271 return val 272 } 273 } 274 275 return 0 276 } 277 278 func lookupFloat64(name string, set *flag.FlagSet) float64 { 279 f := set.Lookup(name) 280 if f != nil { 281 val, err := strconv.ParseFloat(f.Value.String(), 64) 282 if err != nil { 283 return 0 284 } 285 return val 286 } 287 288 return 0 289 } 290 291 func lookupString(name string, set *flag.FlagSet) string { 292 f := set.Lookup(name) 293 if f != nil { 294 return f.Value.String() 295 } 296 297 return "" 298 } 299 300 func lookupStringSlice(name string, set *flag.FlagSet) []string { 301 f := set.Lookup(name) 302 if f != nil { 303 return (f.Value.(*StringSlice)).Value() 304 305 } 306 307 return nil 308 } 309 310 func lookupIntSlice(name string, set *flag.FlagSet) []int { 311 f := set.Lookup(name) 312 if f != nil { 313 return (f.Value.(*IntSlice)).Value() 314 315 } 316 317 return nil 318 } 319 320 func lookupGeneric(name string, set *flag.FlagSet) interface{} { 321 f := set.Lookup(name) 322 if f != nil { 323 return f.Value 324 } 325 return nil 326 } 327 328 func lookupBool(name string, set *flag.FlagSet) bool { 329 f := set.Lookup(name) 330 if f != nil { 331 val, err := strconv.ParseBool(f.Value.String()) 332 if err != nil { 333 return false 334 } 335 return val 336 } 337 338 return false 339 } 340 341 func lookupBoolT(name string, set *flag.FlagSet) bool { 342 f := set.Lookup(name) 343 if f != nil { 344 val, err := strconv.ParseBool(f.Value.String()) 345 if err != nil { 346 return true 347 } 348 return val 349 } 350 351 return false 352 } 353 354 func copyFlag(name string, ff *flag.Flag, set *flag.FlagSet) { 355 switch ff.Value.(type) { 356 case *StringSlice: 357 default: 358 set.Set(name, ff.Value.String()) 359 } 360 } 361 362 func normalizeFlags(flags []Flag, set *flag.FlagSet) error { 363 visited := make(map[string]bool) 364 set.Visit(func(f *flag.Flag) { 365 visited[f.Name] = true 366 }) 367 for _, f := range flags { 368 parts := strings.Split(f.GetName(), ",") 369 if len(parts) == 1 { 370 continue 371 } 372 var ff *flag.Flag 373 for _, name := range parts { 374 name = strings.Trim(name, " ") 375 if visited[name] { 376 if ff != nil { 377 return errors.New("Cannot use two forms of the same flag: " + name + " " + ff.Name) 378 } 379 ff = set.Lookup(name) 380 } 381 } 382 if ff == nil { 383 continue 384 } 385 for _, name := range parts { 386 name = strings.Trim(name, " ") 387 if !visited[name] { 388 copyFlag(name, ff, set) 389 } 390 } 391 } 392 return nil 393 }