github.com/ethxdao/go-ethereum@v0.0.0-20221218102228-5ae34a9cc189/internal/flags/flags.go (about)

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