github.com/klaytn/klaytn@v1.12.1/cmd/utils/customflags.go (about) 1 // Modifications Copyright 2018 The klaytn Authors 2 // Copyright 2015 The go-ethereum Authors 3 // This file is part of go-ethereum. 4 // 5 // go-ethereum is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // go-ethereum is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU General Public License for more details. 14 // 15 // You should have received a copy of the GNU General Public License 16 // along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. 17 // 18 // This file is derived from cmd/utils/customflags.go (2018/06/04). 19 // Modified and improved for the klaytn development. 20 21 package utils 22 23 import ( 24 "encoding" 25 "errors" 26 "flag" 27 "fmt" 28 "math/big" 29 "os" 30 "os/user" 31 "path" 32 "strings" 33 "syscall" 34 35 "github.com/klaytn/klaytn/common/math" 36 "github.com/klaytn/klaytn/datasync/downloader" 37 "github.com/urfave/cli/v2" 38 "github.com/urfave/cli/v2/altsrc" 39 ) 40 41 // NOTE-klaytn: The custom directoryFlag deprecated. 42 // urfave.v2 new flag, PathFlag, replaced the directoryFlag. 43 44 // Custom type which is registered in the flags library which cli uses for 45 // argument parsing. This allows us to expand Value to an absolute path when 46 // the argument is parsed 47 type DirectoryString struct { 48 Value string 49 } 50 51 func (self *DirectoryString) String() string { 52 return self.Value 53 } 54 55 func (self *DirectoryString) Set(value string) error { 56 self.Value = expandPath(value) 57 return nil 58 } 59 60 type WrappedDirectoryFlag struct { 61 DirectoryFlag 62 set *flag.FlagSet 63 } 64 65 func NewWrappedDirectoryFlag(fl DirectoryFlag) *WrappedDirectoryFlag { 66 return &WrappedDirectoryFlag{DirectoryFlag: fl, set: nil} 67 } 68 69 func (f *WrappedDirectoryFlag) Apply(set *flag.FlagSet) { 70 f.set = set 71 f.DirectoryFlag.Apply(set) 72 } 73 74 func (f *WrappedDirectoryFlag) ApplyInputSourceValue(context *cli.Context, isc altsrc.InputSourceContext) error { 75 if f.set != nil { 76 if !isEnvVarSet(f.EnvVar) { 77 value, err := isc.String(f.DirectoryFlag.Name) 78 if err != nil { 79 return err 80 } 81 if value != "" { 82 eachName(f.Name, func(name string) { 83 f.set.Set(f.Name, value) 84 }) 85 } 86 } 87 } 88 return nil 89 } 90 91 // Custom cli.Flag type which expand the received string to an absolute path. 92 // e.g. ~/.ethereum -> /home/username/.ethereum 93 type DirectoryFlag struct { 94 Name string 95 Value DirectoryString 96 Usage string 97 EnvVar string 98 } 99 100 func (self DirectoryFlag) String() string { 101 fmtString := "%s %v\t%v" 102 if len(self.Value.Value) > 0 { 103 fmtString = "%s \"%v\"\t%v" 104 } 105 return fmt.Sprintf(fmtString, prefixedNames(self.Name), self.Value.Value, self.Usage) 106 } 107 108 // called by cli library, grabs variable from environment (if in env) 109 // and adds variable to flag set for parsing. 110 func (self DirectoryFlag) Apply(set *flag.FlagSet) { 111 if self.EnvVar != "" { 112 if envVal, ok := syscall.Getenv(self.EnvVar); ok { 113 self.Value.Value = envVal 114 } 115 } 116 eachName(self.Name, func(name string) { 117 set.Var(&self.Value, self.Name, self.Usage) 118 }) 119 } 120 121 func eachName(longName string, fn func(string)) { 122 parts := strings.Split(longName, ",") 123 for _, name := range parts { 124 name = strings.Trim(name, " ") 125 fn(name) 126 } 127 } 128 129 func isEnvVarSet(envVars string) bool { 130 for _, envVar := range strings.Split(envVars, ",") { 131 envVar = strings.TrimSpace(envVar) 132 if env, ok := syscall.Getenv(envVar); ok { 133 // TODO: Can't use this for bools as 134 // set means that it was true or false based on 135 // Bool flag type, should work for other types 136 logger.Info("env", "env", env) 137 return true 138 } 139 } 140 return false 141 } 142 143 type TextMarshaler interface { 144 encoding.TextMarshaler 145 encoding.TextUnmarshaler 146 } 147 148 // textMarshalerVal turns a TextMarshaler into a flag.Value 149 type textMarshalerVal struct { 150 v TextMarshaler 151 } 152 153 func (v textMarshalerVal) String() string { 154 if v.v == nil { 155 return "" 156 } 157 text, _ := v.v.MarshalText() 158 return string(text) 159 } 160 161 func (v textMarshalerVal) Set(s string) error { 162 return v.v.UnmarshalText([]byte(s)) 163 } 164 165 // TextMarshalerFlag wraps a TextMarshaler value. 166 type TextMarshalerFlag struct { 167 Name string 168 169 Category string 170 Usage string 171 172 Required bool 173 Hidden bool 174 HasBeenSet bool 175 176 Value TextMarshaler 177 Destination *TextMarshaler 178 179 Aliases []string 180 EnvVars []string 181 182 Action func(*cli.Context, TextMarshaler) error 183 } 184 185 // IsSet returns whether or not the flag has been set through env or file 186 func (f *TextMarshalerFlag) IsSet() bool { 187 return f.HasBeenSet 188 } 189 190 // Names returns the names of the flag 191 func (f *TextMarshalerFlag) Names() []string { 192 return cli.FlagNames(f.Name, f.Aliases) 193 } 194 195 // IsRequired returns whether or not the flag is required 196 func (f *TextMarshalerFlag) IsRequired() bool { 197 return f.Required 198 } 199 200 // IsVisible returns true if the flag is not hidden, otherwise false 201 func (f *TextMarshalerFlag) IsVisible() bool { 202 return !f.Hidden 203 } 204 205 func (f *TextMarshalerFlag) String() string { 206 return cli.FlagStringer(f) 207 } 208 209 // TakesValue returns true of the flag takes a value, otherwise false 210 func (f *TextMarshalerFlag) TakesValue() bool { 211 return true 212 } 213 214 // GetUsage returns the usage string for the flag 215 func (f *TextMarshalerFlag) GetUsage() string { 216 return f.Usage 217 } 218 219 // GetCategory returns the category for the flag 220 func (f *TextMarshalerFlag) GetCategory() string { 221 return f.Category 222 } 223 224 // GetValue returns the flags value as string representation and an empty 225 // string if the flag takes no value at all. 226 func (f *TextMarshalerFlag) GetValue() string { 227 return fmt.Sprintf("%q", f.Value) 228 } 229 230 // GetDefaultText returns the default text for this flag 231 func (f *TextMarshalerFlag) GetDefaultText() string { 232 return fmt.Sprintf("%q", f.Value) 233 } 234 235 // GetEnvVars returns the env vars for this flag 236 func (f *TextMarshalerFlag) GetEnvVars() []string { 237 return f.EnvVars 238 } 239 240 func (f *TextMarshalerFlag) Apply(set *flag.FlagSet) error { 241 if f.EnvVars[0] != "" && f.Value != nil { 242 if envVal, ok := syscall.Getenv(f.EnvVars[0]); ok { 243 var mode downloader.SyncMode 244 switch envVal { 245 case "full": 246 mode = downloader.FullSync 247 case "fast": 248 mode = downloader.FastSync 249 case "snap": 250 mode = downloader.SnapSync 251 case "light": 252 mode = downloader.LightSync 253 } 254 f.Value = &mode 255 } 256 } 257 eachName(f.Name, func(name string) { 258 set.Var(textMarshalerVal{f.Value}, f.Name, f.Usage) 259 }) 260 261 return nil 262 } 263 264 // Get returns the flag’s value in the given Context. 265 func (f *TextMarshalerFlag) Get(ctx *cli.Context) string { 266 return ctx.Path(f.Name) 267 } 268 269 // RunAction executes flag action if set 270 func (f *TextMarshalerFlag) RunAction(c *cli.Context) error { 271 if f.Action != nil { 272 return f.Action(c, GlobalTextMarshaler(c, f.Name)) 273 } 274 return nil 275 } 276 277 type WrappedTextMarshalerFlag struct { 278 *TextMarshalerFlag 279 set *flag.FlagSet 280 } 281 282 func NewWrappedTextMarshalerFlag(fl *TextMarshalerFlag) *WrappedTextMarshalerFlag { 283 return &WrappedTextMarshalerFlag{TextMarshalerFlag: fl, set: nil} 284 } 285 286 func (f *WrappedTextMarshalerFlag) Apply(set *flag.FlagSet) error { 287 f.set = set 288 return f.TextMarshalerFlag.Apply(set) 289 } 290 291 func (f *WrappedTextMarshalerFlag) ApplyInputSourceValue(context *cli.Context, isc altsrc.InputSourceContext) error { 292 if f.set != nil { 293 if !context.IsSet(f.Name) && !isEnvVarSet(f.EnvVars[0]) { 294 value, err := isc.String(f.TextMarshalerFlag.Name) 295 if err != nil { 296 return err 297 } 298 if value != "" { 299 eachName(f.Name, func(name string) { 300 f.set.Set(f.Name, value) 301 }) 302 } 303 } 304 } 305 return nil 306 } 307 308 // GlobalTextMarshaler returns the value of a TextMarshalerFlag from the global flag set. 309 func GlobalTextMarshaler(ctx *cli.Context, name string) TextMarshaler { 310 val := ctx.Generic(name) 311 if val == nil { 312 return nil 313 } 314 return val.(textMarshalerVal).v 315 } 316 317 // BigFlag is a command line flag that accepts 256 bit big integers in decimal or 318 // hexadecimal syntax. 319 type BigFlag struct { 320 Name string 321 Value *big.Int 322 Usage string 323 } 324 325 // bigValue turns *big.Int into a flag.Value 326 type bigValue big.Int 327 328 func (b *bigValue) String() string { 329 if b == nil { 330 return "" 331 } 332 return (*big.Int)(b).String() 333 } 334 335 func (b *bigValue) Set(s string) error { 336 int, ok := math.ParseBig256(s) 337 if !ok { 338 return errors.New("invalid integer syntax") 339 } 340 *b = (bigValue)(*int) 341 return nil 342 } 343 344 func (f BigFlag) GetName() string { 345 return f.Name 346 } 347 348 func (f BigFlag) String() string { 349 fmtString := "%s %v\t%v" 350 if f.Value != nil { 351 fmtString = "%s \"%v\"\t%v" 352 } 353 return fmt.Sprintf(fmtString, prefixedNames(f.Name), f.Value, f.Usage) 354 } 355 356 func (f BigFlag) Apply(set *flag.FlagSet) { 357 eachName(f.Name, func(name string) { 358 set.Var((*bigValue)(f.Value), f.Name, f.Usage) 359 }) 360 } 361 362 // GlobalBig returns the value of a BigFlag from the global flag set. 363 func GlobalBig(ctx *cli.Context, name string) *big.Int { 364 val := ctx.Generic(name) 365 if val == nil { 366 return nil 367 } 368 return (*big.Int)(val.(*bigValue)) 369 } 370 371 func prefixFor(name string) (prefix string) { 372 if len(name) == 1 { 373 prefix = "-" 374 } else { 375 prefix = "--" 376 } 377 378 return 379 } 380 381 func prefixedNames(fullName string) (prefixed string) { 382 parts := strings.Split(fullName, ",") 383 for i, name := range parts { 384 name = strings.Trim(name, " ") 385 prefixed += prefixFor(name) + name 386 if i < len(parts)-1 { 387 prefixed += ", " 388 } 389 } 390 return 391 } 392 393 // Expands a file path 394 // 1. replace tilde with users home dir 395 // 2. expands embedded environment variables 396 // 3. cleans the path, e.g. /a/b/../c -> /a/c 397 // Note, it has limitations, e.g. ~someuser/tmp will not be expanded 398 func expandPath(p string) string { 399 if strings.HasPrefix(p, "~/") || strings.HasPrefix(p, "~\\") { 400 if home := homeDir(); home != "" { 401 p = home + p[1:] 402 } 403 } 404 return path.Clean(os.ExpandEnv(p)) 405 } 406 407 func homeDir() string { 408 if home := os.Getenv("HOME"); home != "" { 409 return home 410 } 411 if usr, err := user.Current(); err == nil { 412 return usr.HomeDir 413 } 414 return "" 415 }