github.com/ava-labs/subnet-evm@v0.6.4/internal/flags/flags.go (about) 1 // (c) 2023, Ava Labs, Inc. 2 // 3 // This file is a derived work, based on the go-ethereum library whose original 4 // notices appear below. 5 // 6 // It is distributed under a license compatible with the licensing terms of the 7 // original code from which it is derived. 8 // 9 // Much love to the original authors for their work. 10 // ********** 11 // Copyright 2015 The go-ethereum Authors 12 // This file is part of the go-ethereum library. 13 // 14 // The go-ethereum library is free software: you can redistribute it and/or modify 15 // it under the terms of the GNU Lesser General Public License as published by 16 // the Free Software Foundation, either version 3 of the License, or 17 // (at your option) any later version. 18 // 19 // The go-ethereum library is distributed in the hope that it will be useful, 20 // but WITHOUT ANY WARRANTY; without even the implied warranty of 21 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 // GNU Lesser General Public License for more details. 23 // 24 // You should have received a copy of the GNU Lesser General Public License 25 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 26 27 package flags 28 29 import ( 30 "encoding" 31 "errors" 32 "flag" 33 "fmt" 34 "math/big" 35 "os" 36 "os/user" 37 "path/filepath" 38 "strings" 39 "syscall" 40 41 "github.com/ethereum/go-ethereum/common/math" 42 "github.com/urfave/cli/v2" 43 ) 44 45 // DirectoryString is custom type which is registered in the flags library which cli uses for 46 // argument parsing. This allows us to expand Value to an absolute path when 47 // the argument is parsed 48 type DirectoryString string 49 50 func (s *DirectoryString) String() string { 51 return string(*s) 52 } 53 54 func (s *DirectoryString) Set(value string) error { 55 *s = DirectoryString(expandPath(value)) 56 return nil 57 } 58 59 var ( 60 _ cli.Flag = (*DirectoryFlag)(nil) 61 _ cli.RequiredFlag = (*DirectoryFlag)(nil) 62 _ cli.VisibleFlag = (*DirectoryFlag)(nil) 63 _ cli.DocGenerationFlag = (*DirectoryFlag)(nil) 64 _ cli.CategorizableFlag = (*DirectoryFlag)(nil) 65 ) 66 67 // DirectoryFlag is custom cli.Flag type which expand the received string to an absolute path. 68 // e.g. ~/.ethereum -> /home/username/.ethereum 69 type DirectoryFlag struct { 70 Name string 71 72 Category string 73 DefaultText string 74 Usage string 75 76 Required bool 77 Hidden bool 78 HasBeenSet bool 79 80 Value DirectoryString 81 82 Aliases []string 83 EnvVars []string 84 } 85 86 // For cli.Flag: 87 88 func (f *DirectoryFlag) Names() []string { return append([]string{f.Name}, f.Aliases...) } 89 func (f *DirectoryFlag) IsSet() bool { return f.HasBeenSet } 90 func (f *DirectoryFlag) String() string { return cli.FlagStringer(f) } 91 92 // Apply called by cli library, grabs variable from environment (if in env) 93 // and adds variable to flag set for parsing. 94 func (f *DirectoryFlag) Apply(set *flag.FlagSet) error { 95 for _, envVar := range f.EnvVars { 96 envVar = strings.TrimSpace(envVar) 97 if value, found := syscall.Getenv(envVar); found { 98 f.Value.Set(value) 99 f.HasBeenSet = true 100 break 101 } 102 } 103 eachName(f, func(name string) { 104 set.Var(&f.Value, f.Name, f.Usage) 105 }) 106 return nil 107 } 108 109 // For cli.RequiredFlag: 110 111 func (f *DirectoryFlag) IsRequired() bool { return f.Required } 112 113 // For cli.VisibleFlag: 114 115 func (f *DirectoryFlag) IsVisible() bool { return !f.Hidden } 116 117 // For cli.CategorizableFlag: 118 119 func (f *DirectoryFlag) GetCategory() string { return f.Category } 120 121 // For cli.DocGenerationFlag: 122 123 func (f *DirectoryFlag) TakesValue() bool { return true } 124 func (f *DirectoryFlag) GetUsage() string { return f.Usage } 125 func (f *DirectoryFlag) GetValue() string { return f.Value.String() } 126 func (f *DirectoryFlag) GetEnvVars() []string { return f.EnvVars } 127 128 func (f *DirectoryFlag) GetDefaultText() string { 129 if f.DefaultText != "" { 130 return f.DefaultText 131 } 132 return f.GetValue() 133 } 134 135 type TextMarshaler interface { 136 encoding.TextMarshaler 137 encoding.TextUnmarshaler 138 } 139 140 // textMarshalerVal turns a TextMarshaler into a flag.Value 141 type textMarshalerVal struct { 142 v TextMarshaler 143 } 144 145 func (v textMarshalerVal) String() string { 146 if v.v == nil { 147 return "" 148 } 149 text, _ := v.v.MarshalText() 150 return string(text) 151 } 152 153 func (v textMarshalerVal) Set(s string) error { 154 return v.v.UnmarshalText([]byte(s)) 155 } 156 157 var ( 158 _ cli.Flag = (*TextMarshalerFlag)(nil) 159 _ cli.RequiredFlag = (*TextMarshalerFlag)(nil) 160 _ cli.VisibleFlag = (*TextMarshalerFlag)(nil) 161 _ cli.DocGenerationFlag = (*TextMarshalerFlag)(nil) 162 _ cli.CategorizableFlag = (*TextMarshalerFlag)(nil) 163 ) 164 165 // TextMarshalerFlag wraps a TextMarshaler value. 166 type TextMarshalerFlag struct { 167 Name string 168 169 Category string 170 DefaultText string 171 Usage string 172 173 Required bool 174 Hidden bool 175 HasBeenSet bool 176 177 Value TextMarshaler 178 179 Aliases []string 180 EnvVars []string 181 } 182 183 // For cli.Flag: 184 185 func (f *TextMarshalerFlag) Names() []string { return append([]string{f.Name}, f.Aliases...) } 186 func (f *TextMarshalerFlag) IsSet() bool { return f.HasBeenSet } 187 func (f *TextMarshalerFlag) String() string { return cli.FlagStringer(f) } 188 189 func (f *TextMarshalerFlag) Apply(set *flag.FlagSet) error { 190 for _, envVar := range f.EnvVars { 191 envVar = strings.TrimSpace(envVar) 192 if value, found := syscall.Getenv(envVar); found { 193 if err := f.Value.UnmarshalText([]byte(value)); err != nil { 194 return fmt.Errorf("could not parse %q from environment variable %q for flag %s: %s", value, envVar, f.Name, err) 195 } 196 f.HasBeenSet = true 197 break 198 } 199 } 200 eachName(f, func(name string) { 201 set.Var(textMarshalerVal{f.Value}, f.Name, f.Usage) 202 }) 203 return nil 204 } 205 206 // For cli.RequiredFlag: 207 208 func (f *TextMarshalerFlag) IsRequired() bool { return f.Required } 209 210 // For cli.VisibleFlag: 211 212 func (f *TextMarshalerFlag) IsVisible() bool { return !f.Hidden } 213 214 // For cli.CategorizableFlag: 215 216 func (f *TextMarshalerFlag) GetCategory() string { return f.Category } 217 218 // For cli.DocGenerationFlag: 219 220 func (f *TextMarshalerFlag) TakesValue() bool { return true } 221 func (f *TextMarshalerFlag) GetUsage() string { return f.Usage } 222 func (f *TextMarshalerFlag) GetEnvVars() []string { return f.EnvVars } 223 224 func (f *TextMarshalerFlag) GetValue() string { 225 t, err := f.Value.MarshalText() 226 if err != nil { 227 return "(ERR: " + err.Error() + ")" 228 } 229 return string(t) 230 } 231 232 func (f *TextMarshalerFlag) GetDefaultText() string { 233 if f.DefaultText != "" { 234 return f.DefaultText 235 } 236 return f.GetValue() 237 } 238 239 // GlobalTextMarshaler returns the value of a TextMarshalerFlag from the global flag set. 240 func GlobalTextMarshaler(ctx *cli.Context, name string) TextMarshaler { 241 val := ctx.Generic(name) 242 if val == nil { 243 return nil 244 } 245 return val.(textMarshalerVal).v 246 } 247 248 var ( 249 _ cli.Flag = (*BigFlag)(nil) 250 _ cli.RequiredFlag = (*BigFlag)(nil) 251 _ cli.VisibleFlag = (*BigFlag)(nil) 252 _ cli.DocGenerationFlag = (*BigFlag)(nil) 253 _ cli.CategorizableFlag = (*BigFlag)(nil) 254 ) 255 256 // BigFlag is a command line flag that accepts 256 bit big integers in decimal or 257 // hexadecimal syntax. 258 type BigFlag struct { 259 Name string 260 261 Category string 262 DefaultText string 263 Usage string 264 265 Required bool 266 Hidden bool 267 HasBeenSet bool 268 269 Value *big.Int 270 271 Aliases []string 272 EnvVars []string 273 } 274 275 // For cli.Flag: 276 277 func (f *BigFlag) Names() []string { return append([]string{f.Name}, f.Aliases...) } 278 func (f *BigFlag) IsSet() bool { return f.HasBeenSet } 279 func (f *BigFlag) String() string { return cli.FlagStringer(f) } 280 281 func (f *BigFlag) Apply(set *flag.FlagSet) error { 282 for _, envVar := range f.EnvVars { 283 envVar = strings.TrimSpace(envVar) 284 if value, found := syscall.Getenv(envVar); found { 285 if _, ok := f.Value.SetString(value, 10); !ok { 286 return fmt.Errorf("could not parse %q from environment variable %q for flag %s", value, envVar, f.Name) 287 } 288 f.HasBeenSet = true 289 break 290 } 291 } 292 eachName(f, func(name string) { 293 f.Value = new(big.Int) 294 set.Var((*bigValue)(f.Value), f.Name, f.Usage) 295 }) 296 297 return nil 298 } 299 300 // For cli.RequiredFlag: 301 302 func (f *BigFlag) IsRequired() bool { return f.Required } 303 304 // For cli.VisibleFlag: 305 306 func (f *BigFlag) IsVisible() bool { return !f.Hidden } 307 308 // For cli.CategorizableFlag: 309 310 func (f *BigFlag) GetCategory() string { return f.Category } 311 312 // For cli.DocGenerationFlag: 313 314 func (f *BigFlag) TakesValue() bool { return true } 315 func (f *BigFlag) GetUsage() string { return f.Usage } 316 func (f *BigFlag) GetValue() string { return f.Value.String() } 317 func (f *BigFlag) GetEnvVars() []string { return f.EnvVars } 318 319 func (f *BigFlag) GetDefaultText() string { 320 if f.DefaultText != "" { 321 return f.DefaultText 322 } 323 return f.GetValue() 324 } 325 326 // bigValue turns *big.Int into a flag.Value 327 type bigValue big.Int 328 329 func (b *bigValue) String() string { 330 if b == nil { 331 return "" 332 } 333 return (*big.Int)(b).String() 334 } 335 336 func (b *bigValue) Set(s string) error { 337 intVal, ok := math.ParseBig256(s) 338 if !ok { 339 return errors.New("invalid integer syntax") 340 } 341 *b = (bigValue)(*intVal) 342 return nil 343 } 344 345 // GlobalBig returns the value of a BigFlag from the global flag set. 346 func GlobalBig(ctx *cli.Context, name string) *big.Int { 347 val := ctx.Generic(name) 348 if val == nil { 349 return nil 350 } 351 return (*big.Int)(val.(*bigValue)) 352 } 353 354 // Expands a file path 355 // 1. replace tilde with users home dir 356 // 2. expands embedded environment variables 357 // 3. cleans the path, e.g. /a/b/../c -> /a/c 358 // Note, it has limitations, e.g. ~someuser/tmp will not be expanded 359 func expandPath(p string) string { 360 // Named pipes are not file paths on windows, ignore 361 if strings.HasPrefix(p, `\\.\pipe`) { 362 return p 363 } 364 if strings.HasPrefix(p, "~/") || strings.HasPrefix(p, "~\\") { 365 if home := HomeDir(); home != "" { 366 p = home + p[1:] 367 } 368 } 369 return filepath.Clean(os.ExpandEnv(p)) 370 } 371 372 func HomeDir() string { 373 if home := os.Getenv("HOME"); home != "" { 374 return home 375 } 376 if usr, err := user.Current(); err == nil { 377 return usr.HomeDir 378 } 379 return "" 380 } 381 382 func eachName(f cli.Flag, fn func(string)) { 383 for _, name := range f.Names() { 384 name = strings.Trim(name, " ") 385 fn(name) 386 } 387 }