github.com/ethw3/go-ethereuma@v0.0.0-20221013053120-c14602a4c23c/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/ethw3/go-ethereuma/params" 24 "github.com/urfave/cli/v2" 25 ) 26 27 // NewApp creates an app with sane defaults. 28 func NewApp(gitCommit, gitDate, usage string) *cli.App { 29 app := cli.NewApp() 30 app.EnableBashCompletion = true 31 app.Version = params.VersionWithCommit(gitCommit, gitDate) 32 app.Usage = usage 33 app.Copyright = "Copyright 2013-2022 The go-ethereum Authors" 34 app.Before = func(ctx *cli.Context) error { 35 MigrateGlobalFlags(ctx) 36 return nil 37 } 38 return app 39 } 40 41 // Merge merges the given flag slices. 42 func Merge(groups ...[]cli.Flag) []cli.Flag { 43 var ret []cli.Flag 44 for _, group := range groups { 45 ret = append(ret, group...) 46 } 47 return ret 48 } 49 50 var migrationApplied = map[*cli.Command]struct{}{} 51 52 // MigrateGlobalFlags makes all global flag values available in the 53 // context. This should be called as early as possible in app.Before. 54 // 55 // Example: 56 // 57 // geth account new --keystore /tmp/mykeystore --lightkdf 58 // 59 // is equivalent after calling this method with: 60 // 61 // geth --keystore /tmp/mykeystore --lightkdf account new 62 // 63 // i.e. in the subcommand Action function of 'account new', ctx.Bool("lightkdf) 64 // will return true even if --lightkdf is set as a global option. 65 // 66 // This function may become unnecessary when https://github.com/urfave/cli/pull/1245 is merged. 67 func MigrateGlobalFlags(ctx *cli.Context) { 68 var iterate func(cs []*cli.Command, fn func(*cli.Command)) 69 iterate = func(cs []*cli.Command, fn func(*cli.Command)) { 70 for _, cmd := range cs { 71 if _, ok := migrationApplied[cmd]; ok { 72 continue 73 } 74 migrationApplied[cmd] = struct{}{} 75 fn(cmd) 76 iterate(cmd.Subcommands, fn) 77 } 78 } 79 80 // This iterates over all commands and wraps their action function. 81 iterate(ctx.App.Commands, func(cmd *cli.Command) { 82 if cmd.Action == nil { 83 return 84 } 85 86 action := cmd.Action 87 cmd.Action = func(ctx *cli.Context) error { 88 doMigrateFlags(ctx) 89 return action(ctx) 90 } 91 }) 92 } 93 94 func doMigrateFlags(ctx *cli.Context) { 95 for _, name := range ctx.FlagNames() { 96 for _, parent := range ctx.Lineage()[1:] { 97 if parent.IsSet(name) { 98 ctx.Set(name, parent.String(name)) 99 break 100 } 101 } 102 } 103 } 104 105 func init() { 106 cli.FlagStringer = FlagString 107 } 108 109 // FlagString prints a single flag in help. 110 func FlagString(f cli.Flag) string { 111 df, ok := f.(cli.DocGenerationFlag) 112 if !ok { 113 return "" 114 } 115 116 needsPlaceholder := df.TakesValue() 117 placeholder := "" 118 if needsPlaceholder { 119 placeholder = "value" 120 } 121 122 namesText := pad(cli.FlagNamePrefixer(df.Names(), placeholder), 30) 123 124 defaultValueString := "" 125 if s := df.GetDefaultText(); s != "" { 126 defaultValueString = " (default: " + s + ")" 127 } 128 129 usage := strings.TrimSpace(df.GetUsage()) 130 envHint := strings.TrimSpace(cli.FlagEnvHinter(df.GetEnvVars(), "")) 131 if len(envHint) > 0 { 132 usage += " " + envHint 133 } 134 135 usage = wordWrap(usage, 80) 136 usage = indent(usage, 10) 137 138 return fmt.Sprintf("\n %s%s\n%s", namesText, defaultValueString, usage) 139 } 140 141 func pad(s string, length int) string { 142 if len(s) < length { 143 s += strings.Repeat(" ", length-len(s)) 144 } 145 return s 146 } 147 148 func indent(s string, nspace int) string { 149 ind := strings.Repeat(" ", nspace) 150 return ind + strings.ReplaceAll(s, "\n", "\n"+ind) 151 } 152 153 func wordWrap(s string, width int) string { 154 var ( 155 output strings.Builder 156 lineLength = 0 157 ) 158 159 for { 160 sp := strings.IndexByte(s, ' ') 161 var word string 162 if sp == -1 { 163 word = s 164 } else { 165 word = s[:sp] 166 } 167 wlen := len(word) 168 over := lineLength+wlen >= width 169 if over { 170 output.WriteByte('\n') 171 lineLength = 0 172 } else { 173 if lineLength != 0 { 174 output.WriteByte(' ') 175 lineLength++ 176 } 177 } 178 179 output.WriteString(word) 180 lineLength += wlen 181 182 if sp == -1 { 183 break 184 } 185 s = s[wlen+1:] 186 } 187 188 return output.String() 189 }