github.com/carter-ya/go-ethereum@v0.0.0-20230628080049-d2309be3983b/internal/flags/helpers.go (about) 1 // Copyright 2020 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 "fmt" 21 "strings" 22 23 "github.com/ethereum/go-ethereum/internal/version" 24 "github.com/ethereum/go-ethereum/params" 25 "github.com/urfave/cli/v2" 26 ) 27 28 // NewApp creates an app with sane defaults. 29 func NewApp(usage string) *cli.App { 30 git, _ := version.VCS() 31 app := cli.NewApp() 32 app.EnableBashCompletion = true 33 app.Version = params.VersionWithCommit(git.Commit, git.Date) 34 app.Usage = usage 35 app.Copyright = "Copyright 2013-2022 The go-ethereum Authors" 36 app.Before = func(ctx *cli.Context) error { 37 MigrateGlobalFlags(ctx) 38 return nil 39 } 40 return app 41 } 42 43 // Merge merges the given flag slices. 44 func Merge(groups ...[]cli.Flag) []cli.Flag { 45 var ret []cli.Flag 46 for _, group := range groups { 47 ret = append(ret, group...) 48 } 49 return ret 50 } 51 52 var migrationApplied = map[*cli.Command]struct{}{} 53 54 // MigrateGlobalFlags makes all global flag values available in the 55 // context. This should be called as early as possible in app.Before. 56 // 57 // Example: 58 // 59 // geth account new --keystore /tmp/mykeystore --lightkdf 60 // 61 // is equivalent after calling this method with: 62 // 63 // geth --keystore /tmp/mykeystore --lightkdf account new 64 // 65 // i.e. in the subcommand Action function of 'account new', ctx.Bool("lightkdf) 66 // will return true even if --lightkdf is set as a global option. 67 // 68 // This function may become unnecessary when https://github.com/urfave/cli/pull/1245 is merged. 69 func MigrateGlobalFlags(ctx *cli.Context) { 70 var iterate func(cs []*cli.Command, fn func(*cli.Command)) 71 iterate = func(cs []*cli.Command, fn func(*cli.Command)) { 72 for _, cmd := range cs { 73 if _, ok := migrationApplied[cmd]; ok { 74 continue 75 } 76 migrationApplied[cmd] = struct{}{} 77 fn(cmd) 78 iterate(cmd.Subcommands, fn) 79 } 80 } 81 82 // This iterates over all commands and wraps their action function. 83 iterate(ctx.App.Commands, func(cmd *cli.Command) { 84 if cmd.Action == nil { 85 return 86 } 87 88 action := cmd.Action 89 cmd.Action = func(ctx *cli.Context) error { 90 doMigrateFlags(ctx) 91 return action(ctx) 92 } 93 }) 94 } 95 96 func doMigrateFlags(ctx *cli.Context) { 97 // Figure out if there are any aliases of commands. If there are, we want 98 // to ignore them when iterating over the flags. 99 var aliases = make(map[string]bool) 100 for _, fl := range ctx.Command.Flags { 101 for _, alias := range fl.Names()[1:] { 102 aliases[alias] = true 103 } 104 } 105 for _, name := range ctx.FlagNames() { 106 for _, parent := range ctx.Lineage()[1:] { 107 if parent.IsSet(name) { 108 // When iterating across the lineage, we will be served both 109 // the 'canon' and alias formats of all commmands. In most cases, 110 // it's fine to set it in the ctx multiple times (one for each 111 // name), however, the Slice-flags are not fine. 112 // The slice-flags accumulate, so if we set it once as 113 // "foo" and once as alias "F", then both will be present in the slice. 114 if _, isAlias := aliases[name]; isAlias { 115 continue 116 } 117 // If it is a string-slice, we need to set it as 118 // "alfa, beta, gamma" instead of "[alfa beta gamma]", in order 119 // for the backing StringSlice to parse it properly. 120 if result := parent.StringSlice(name); len(result) > 0 { 121 ctx.Set(name, strings.Join(result, ",")) 122 } else { 123 ctx.Set(name, parent.String(name)) 124 } 125 break 126 } 127 } 128 } 129 } 130 131 func init() { 132 cli.FlagStringer = FlagString 133 } 134 135 // FlagString prints a single flag in help. 136 func FlagString(f cli.Flag) string { 137 df, ok := f.(cli.DocGenerationFlag) 138 if !ok { 139 return "" 140 } 141 142 needsPlaceholder := df.TakesValue() 143 placeholder := "" 144 if needsPlaceholder { 145 placeholder = "value" 146 } 147 148 namesText := pad(cli.FlagNamePrefixer(df.Names(), placeholder), 30) 149 150 defaultValueString := "" 151 if s := df.GetDefaultText(); s != "" { 152 defaultValueString = " (default: " + s + ")" 153 } 154 155 usage := strings.TrimSpace(df.GetUsage()) 156 envHint := strings.TrimSpace(cli.FlagEnvHinter(df.GetEnvVars(), "")) 157 if len(envHint) > 0 { 158 usage += " " + envHint 159 } 160 161 usage = wordWrap(usage, 80) 162 usage = indent(usage, 10) 163 164 return fmt.Sprintf("\n %s%s\n%s", namesText, defaultValueString, usage) 165 } 166 167 func pad(s string, length int) string { 168 if len(s) < length { 169 s += strings.Repeat(" ", length-len(s)) 170 } 171 return s 172 } 173 174 func indent(s string, nspace int) string { 175 ind := strings.Repeat(" ", nspace) 176 return ind + strings.ReplaceAll(s, "\n", "\n"+ind) 177 } 178 179 func wordWrap(s string, width int) string { 180 var ( 181 output strings.Builder 182 lineLength = 0 183 ) 184 185 for { 186 sp := strings.IndexByte(s, ' ') 187 var word string 188 if sp == -1 { 189 word = s 190 } else { 191 word = s[:sp] 192 } 193 wlen := len(word) 194 over := lineLength+wlen >= width 195 if over { 196 output.WriteByte('\n') 197 lineLength = 0 198 } else { 199 if lineLength != 0 { 200 output.WriteByte(' ') 201 lineLength++ 202 } 203 } 204 205 output.WriteString(word) 206 lineLength += wlen 207 208 if sp == -1 { 209 break 210 } 211 s = s[wlen+1:] 212 } 213 214 return output.String() 215 }