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