github.com/klaytn/klaytn@v1.10.2/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  	"gopkg.in/urfave/cli.v1"
    38  	"gopkg.in/urfave/cli.v1/altsrc"
    39  )
    40  
    41  // Custom type which is registered in the flags library which cli uses for
    42  // argument parsing. This allows us to expand Value to an absolute path when
    43  // the argument is parsed
    44  type DirectoryString struct {
    45  	Value string
    46  }
    47  
    48  func (self *DirectoryString) String() string {
    49  	return self.Value
    50  }
    51  
    52  func (self *DirectoryString) Set(value string) error {
    53  	self.Value = expandPath(value)
    54  	return nil
    55  }
    56  
    57  type WrappedDirectoryFlag struct {
    58  	DirectoryFlag
    59  	set *flag.FlagSet
    60  }
    61  
    62  func NewWrappedDirectoryFlag(fl DirectoryFlag) *WrappedDirectoryFlag {
    63  	return &WrappedDirectoryFlag{DirectoryFlag: fl, set: nil}
    64  }
    65  
    66  func (f *WrappedDirectoryFlag) Apply(set *flag.FlagSet) {
    67  	f.set = set
    68  	f.DirectoryFlag.Apply(set)
    69  }
    70  
    71  func (f *WrappedDirectoryFlag) ApplyInputSourceValue(context *cli.Context, isc altsrc.InputSourceContext) error {
    72  	if f.set != nil {
    73  		if !isEnvVarSet(f.EnvVar) {
    74  			value, err := isc.String(f.DirectoryFlag.Name)
    75  			if err != nil {
    76  				return err
    77  			}
    78  			if value != "" {
    79  				eachName(f.Name, func(name string) {
    80  					f.set.Set(f.Name, value)
    81  				})
    82  			}
    83  		}
    84  	}
    85  	return nil
    86  }
    87  
    88  func isEnvVarSet(envVars string) bool {
    89  	for _, envVar := range strings.Split(envVars, ",") {
    90  		envVar = strings.TrimSpace(envVar)
    91  		if env, ok := syscall.Getenv(envVar); ok {
    92  			// TODO: Can't use this for bools as
    93  			// set means that it was true or false based on
    94  			// Bool flag type, should work for other types
    95  			logger.Info("env", "env", env)
    96  			return true
    97  		}
    98  	}
    99  	return false
   100  }
   101  
   102  // Custom cli.Flag type which expand the received string to an absolute path.
   103  // e.g. ~/.ethereum -> /home/username/.ethereum
   104  type DirectoryFlag struct {
   105  	Name   string
   106  	Value  DirectoryString
   107  	Usage  string
   108  	EnvVar string
   109  }
   110  
   111  func (self DirectoryFlag) String() string {
   112  	fmtString := "%s %v\t%v"
   113  	if len(self.Value.Value) > 0 {
   114  		fmtString = "%s \"%v\"\t%v"
   115  	}
   116  	return fmt.Sprintf(fmtString, prefixedNames(self.Name), self.Value.Value, self.Usage)
   117  }
   118  
   119  func eachName(longName string, fn func(string)) {
   120  	parts := strings.Split(longName, ",")
   121  	for _, name := range parts {
   122  		name = strings.Trim(name, " ")
   123  		fn(name)
   124  	}
   125  }
   126  
   127  // called by cli library, grabs variable from environment (if in env)
   128  // and adds variable to flag set for parsing.
   129  func (self DirectoryFlag) Apply(set *flag.FlagSet) {
   130  	if self.EnvVar != "" {
   131  		if envVal, ok := syscall.Getenv(self.EnvVar); ok {
   132  			self.Value.Value = envVal
   133  		}
   134  	}
   135  	eachName(self.Name, func(name string) {
   136  		set.Var(&self.Value, self.Name, self.Usage)
   137  	})
   138  }
   139  
   140  type TextMarshaler interface {
   141  	encoding.TextMarshaler
   142  	encoding.TextUnmarshaler
   143  }
   144  
   145  // textMarshalerVal turns a TextMarshaler into a flag.Value
   146  type textMarshalerVal struct {
   147  	v TextMarshaler
   148  }
   149  
   150  func (v textMarshalerVal) String() string {
   151  	if v.v == nil {
   152  		return ""
   153  	}
   154  	text, _ := v.v.MarshalText()
   155  	return string(text)
   156  }
   157  
   158  func (v textMarshalerVal) Set(s string) error {
   159  	return v.v.UnmarshalText([]byte(s))
   160  }
   161  
   162  // TextMarshalerFlag wraps a TextMarshaler value.
   163  type TextMarshalerFlag struct {
   164  	Name   string
   165  	Value  TextMarshaler
   166  	Usage  string
   167  	EnvVar string
   168  }
   169  
   170  func (f TextMarshalerFlag) GetName() string {
   171  	return f.Name
   172  }
   173  
   174  func (f TextMarshalerFlag) String() string {
   175  	return fmt.Sprintf("%s \"%v\"\t%v", prefixedNames(f.Name), f.Value, f.Usage)
   176  }
   177  
   178  func (f TextMarshalerFlag) Apply(set *flag.FlagSet) {
   179  	if f.EnvVar != "" && f.Value != nil {
   180  		if envVal, ok := syscall.Getenv(f.EnvVar); ok {
   181  			var mode downloader.SyncMode
   182  			switch envVal {
   183  			case "full":
   184  				mode = downloader.FullSync
   185  			case "fast":
   186  				mode = downloader.FastSync
   187  			case "snap":
   188  				mode = downloader.SnapSync
   189  			case "light":
   190  				mode = downloader.LightSync
   191  			}
   192  			f.Value = &mode
   193  		}
   194  	}
   195  	eachName(f.Name, func(name string) {
   196  		set.Var(textMarshalerVal{f.Value}, f.Name, f.Usage)
   197  	})
   198  }
   199  
   200  type WrappedTextMarshalerFlag struct {
   201  	TextMarshalerFlag
   202  	set *flag.FlagSet
   203  }
   204  
   205  func NewWrappedTextMarshalerFlag(fl TextMarshalerFlag) *WrappedTextMarshalerFlag {
   206  	return &WrappedTextMarshalerFlag{TextMarshalerFlag: fl, set: nil}
   207  }
   208  
   209  func (f *WrappedTextMarshalerFlag) Apply(set *flag.FlagSet) {
   210  	f.set = set
   211  	f.TextMarshalerFlag.Apply(set)
   212  }
   213  
   214  func (f *WrappedTextMarshalerFlag) ApplyInputSourceValue(context *cli.Context, isc altsrc.InputSourceContext) error {
   215  	if f.set != nil {
   216  		if !context.IsSet(f.Name) && !isEnvVarSet(f.EnvVar) {
   217  			value, err := isc.String(f.TextMarshalerFlag.Name)
   218  			if err != nil {
   219  				return err
   220  			}
   221  			if value != "" {
   222  				eachName(f.Name, func(name string) {
   223  					f.set.Set(f.Name, value)
   224  				})
   225  			}
   226  		}
   227  	}
   228  	return nil
   229  }
   230  
   231  // GlobalTextMarshaler returns the value of a TextMarshalerFlag from the global flag set.
   232  func GlobalTextMarshaler(ctx *cli.Context, name string) TextMarshaler {
   233  	val := ctx.GlobalGeneric(name)
   234  	if val == nil {
   235  		return nil
   236  	}
   237  	return val.(textMarshalerVal).v
   238  }
   239  
   240  // BigFlag is a command line flag that accepts 256 bit big integers in decimal or
   241  // hexadecimal syntax.
   242  type BigFlag struct {
   243  	Name  string
   244  	Value *big.Int
   245  	Usage string
   246  }
   247  
   248  // bigValue turns *big.Int into a flag.Value
   249  type bigValue big.Int
   250  
   251  func (b *bigValue) String() string {
   252  	if b == nil {
   253  		return ""
   254  	}
   255  	return (*big.Int)(b).String()
   256  }
   257  
   258  func (b *bigValue) Set(s string) error {
   259  	int, ok := math.ParseBig256(s)
   260  	if !ok {
   261  		return errors.New("invalid integer syntax")
   262  	}
   263  	*b = (bigValue)(*int)
   264  	return nil
   265  }
   266  
   267  func (f BigFlag) GetName() string {
   268  	return f.Name
   269  }
   270  
   271  func (f BigFlag) String() string {
   272  	fmtString := "%s %v\t%v"
   273  	if f.Value != nil {
   274  		fmtString = "%s \"%v\"\t%v"
   275  	}
   276  	return fmt.Sprintf(fmtString, prefixedNames(f.Name), f.Value, f.Usage)
   277  }
   278  
   279  func (f BigFlag) Apply(set *flag.FlagSet) {
   280  	eachName(f.Name, func(name string) {
   281  		set.Var((*bigValue)(f.Value), f.Name, f.Usage)
   282  	})
   283  }
   284  
   285  // GlobalBig returns the value of a BigFlag from the global flag set.
   286  func GlobalBig(ctx *cli.Context, name string) *big.Int {
   287  	val := ctx.GlobalGeneric(name)
   288  	if val == nil {
   289  		return nil
   290  	}
   291  	return (*big.Int)(val.(*bigValue))
   292  }
   293  
   294  func prefixFor(name string) (prefix string) {
   295  	if len(name) == 1 {
   296  		prefix = "-"
   297  	} else {
   298  		prefix = "--"
   299  	}
   300  
   301  	return
   302  }
   303  
   304  func prefixedNames(fullName string) (prefixed string) {
   305  	parts := strings.Split(fullName, ",")
   306  	for i, name := range parts {
   307  		name = strings.Trim(name, " ")
   308  		prefixed += prefixFor(name) + name
   309  		if i < len(parts)-1 {
   310  			prefixed += ", "
   311  		}
   312  	}
   313  	return
   314  }
   315  
   316  func (self DirectoryFlag) GetName() string {
   317  	return self.Name
   318  }
   319  
   320  func (self *DirectoryFlag) Set(value string) {
   321  	self.Value.Value = value
   322  }
   323  
   324  // Expands a file path
   325  // 1. replace tilde with users home dir
   326  // 2. expands embedded environment variables
   327  // 3. cleans the path, e.g. /a/b/../c -> /a/c
   328  // Note, it has limitations, e.g. ~someuser/tmp will not be expanded
   329  func expandPath(p string) string {
   330  	if strings.HasPrefix(p, "~/") || strings.HasPrefix(p, "~\\") {
   331  		if home := homeDir(); home != "" {
   332  			p = home + p[1:]
   333  		}
   334  	}
   335  	return path.Clean(os.ExpandEnv(p))
   336  }
   337  
   338  func homeDir() string {
   339  	if home := os.Getenv("HOME"); home != "" {
   340  		return home
   341  	}
   342  	if usr, err := user.Current(); err == nil {
   343  		return usr.HomeDir
   344  	}
   345  	return ""
   346  }