github.com/arieschain/arieschain@v0.0.0-20191023063405-37c074544356/cmd/utils/customflags.go (about) 1 2 package utils 3 4 import ( 5 "encoding" 6 "errors" 7 "flag" 8 "fmt" 9 "math/big" 10 "os" 11 "os/user" 12 "path" 13 "strings" 14 15 "github.com/quickchainproject/quickchain/common/math" 16 "gopkg.in/urfave/cli.v1" 17 ) 18 19 // Custom type which is registered in the flags library which cli uses for 20 // argument parsing. This allows us to expand Value to an absolute path when 21 // the argument is parsed 22 type DirectoryString struct { 23 Value string 24 } 25 26 func (self *DirectoryString) String() string { 27 return self.Value 28 } 29 30 func (self *DirectoryString) Set(value string) error { 31 self.Value = expandPath(value) 32 return nil 33 } 34 35 // Custom cli.Flag type which expand the received string to an absolute path. 36 // e.g. ~/.quickchain -> /home/username/.quickchain 37 type DirectoryFlag struct { 38 Name string 39 Value DirectoryString 40 Usage string 41 } 42 43 func (self DirectoryFlag) String() string { 44 fmtString := "%s %v\t%v" 45 if len(self.Value.Value) > 0 { 46 fmtString = "%s \"%v\"\t%v" 47 } 48 return fmt.Sprintf(fmtString, prefixedNames(self.Name), self.Value.Value, self.Usage) 49 } 50 51 func eachName(longName string, fn func(string)) { 52 parts := strings.Split(longName, ",") 53 for _, name := range parts { 54 name = strings.Trim(name, " ") 55 fn(name) 56 } 57 } 58 59 // called by cli library, grabs variable from environment (if in env) 60 // and adds variable to flag set for parsing. 61 func (self DirectoryFlag) Apply(set *flag.FlagSet) { 62 eachName(self.Name, func(name string) { 63 set.Var(&self.Value, self.Name, self.Usage) 64 }) 65 } 66 67 type TextMarshaler interface { 68 encoding.TextMarshaler 69 encoding.TextUnmarshaler 70 } 71 72 // textMarshalerVal turns a TextMarshaler into a flag.Value 73 type textMarshalerVal struct { 74 v TextMarshaler 75 } 76 77 func (v textMarshalerVal) String() string { 78 if v.v == nil { 79 return "" 80 } 81 text, _ := v.v.MarshalText() 82 return string(text) 83 } 84 85 func (v textMarshalerVal) Set(s string) error { 86 return v.v.UnmarshalText([]byte(s)) 87 } 88 89 // TextMarshalerFlag wraps a TextMarshaler value. 90 type TextMarshalerFlag struct { 91 Name string 92 Value TextMarshaler 93 Usage string 94 } 95 96 func (f TextMarshalerFlag) GetName() string { 97 return f.Name 98 } 99 100 func (f TextMarshalerFlag) String() string { 101 return fmt.Sprintf("%s \"%v\"\t%v", prefixedNames(f.Name), f.Value, f.Usage) 102 } 103 104 func (f TextMarshalerFlag) Apply(set *flag.FlagSet) { 105 eachName(f.Name, func(name string) { 106 set.Var(textMarshalerVal{f.Value}, f.Name, f.Usage) 107 }) 108 } 109 110 // GlobalTextMarshaler returns the value of a TextMarshalerFlag from the global flag set. 111 func GlobalTextMarshaler(ctx *cli.Context, name string) TextMarshaler { 112 val := ctx.GlobalGeneric(name) 113 if val == nil { 114 return nil 115 } 116 return val.(textMarshalerVal).v 117 } 118 119 // BigFlag is a command line flag that accepts 256 bit big integers in decimal or 120 // hexadecimal syntax. 121 type BigFlag struct { 122 Name string 123 Value *big.Int 124 Usage string 125 } 126 127 // bigValue turns *big.Int into a flag.Value 128 type bigValue big.Int 129 130 func (b *bigValue) String() string { 131 if b == nil { 132 return "" 133 } 134 return (*big.Int)(b).String() 135 } 136 137 func (b *bigValue) Set(s string) error { 138 int, ok := math.ParseBig256(s) 139 if !ok { 140 return errors.New("invalid integer syntax") 141 } 142 *b = (bigValue)(*int) 143 return nil 144 } 145 146 func (f BigFlag) GetName() string { 147 return f.Name 148 } 149 150 func (f BigFlag) String() string { 151 fmtString := "%s %v\t%v" 152 if f.Value != nil { 153 fmtString = "%s \"%v\"\t%v" 154 } 155 return fmt.Sprintf(fmtString, prefixedNames(f.Name), f.Value, f.Usage) 156 } 157 158 func (f BigFlag) Apply(set *flag.FlagSet) { 159 eachName(f.Name, func(name string) { 160 set.Var((*bigValue)(f.Value), f.Name, f.Usage) 161 }) 162 } 163 164 // GlobalBig returns the value of a BigFlag from the global flag set. 165 func GlobalBig(ctx *cli.Context, name string) *big.Int { 166 val := ctx.GlobalGeneric(name) 167 if val == nil { 168 return nil 169 } 170 return (*big.Int)(val.(*bigValue)) 171 } 172 173 func prefixFor(name string) (prefix string) { 174 if len(name) == 1 { 175 prefix = "-" 176 } else { 177 prefix = "--" 178 } 179 180 return 181 } 182 183 func prefixedNames(fullName string) (prefixed string) { 184 parts := strings.Split(fullName, ",") 185 for i, name := range parts { 186 name = strings.Trim(name, " ") 187 prefixed += prefixFor(name) + name 188 if i < len(parts)-1 { 189 prefixed += ", " 190 } 191 } 192 return 193 } 194 195 func (self DirectoryFlag) GetName() string { 196 return self.Name 197 } 198 199 func (self *DirectoryFlag) Set(value string) { 200 self.Value.Value = value 201 } 202 203 // Expands a file path 204 // 1. replace tilde with users home dir 205 // 2. expands embedded environment variables 206 // 3. cleans the path, e.g. /a/b/../c -> /a/c 207 // Note, it has limitations, e.g. ~someuser/tmp will not be expanded 208 func expandPath(p string) string { 209 if strings.HasPrefix(p, "~/") || strings.HasPrefix(p, "~\\") { 210 if home := homeDir(); home != "" { 211 p = home + p[1:] 212 } 213 } 214 return path.Clean(os.ExpandEnv(p)) 215 } 216 217 func homeDir() string { 218 if home := os.Getenv("HOME"); home != "" { 219 return home 220 } 221 if usr, err := user.Current(); err == nil { 222 return usr.HomeDir 223 } 224 return "" 225 }