go.etcd.io/etcd@v3.3.27+incompatible/functional/runner/help.go (about) 1 // Copyright 2015 The etcd Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // copied from https://github.com/rkt/rkt/blob/master/rkt/help.go 16 17 package runner 18 19 import ( 20 "bytes" 21 "fmt" 22 "io" 23 "os" 24 "strings" 25 "text/tabwriter" 26 "text/template" 27 28 "github.com/coreos/etcd/version" 29 30 "github.com/spf13/cobra" 31 "github.com/spf13/pflag" 32 ) 33 34 var ( 35 commandUsageTemplate *template.Template 36 templFuncs = template.FuncMap{ 37 "descToLines": func(s string) []string { 38 // trim leading/trailing whitespace and split into slice of lines 39 return strings.Split(strings.Trim(s, "\n\t "), "\n") 40 }, 41 "cmdName": func(cmd *cobra.Command, startCmd *cobra.Command) string { 42 parts := []string{cmd.Name()} 43 for cmd.HasParent() && cmd.Parent().Name() != startCmd.Name() { 44 cmd = cmd.Parent() 45 parts = append([]string{cmd.Name()}, parts...) 46 } 47 return strings.Join(parts, " ") 48 }, 49 } 50 ) 51 52 func init() { 53 commandUsage := ` 54 {{ $cmd := .Cmd }}\ 55 {{ $cmdname := cmdName .Cmd .Cmd.Root }}\ 56 NAME: 57 {{ if not .Cmd.HasParent }}\ 58 {{printf "\t%s - %s" .Cmd.Name .Cmd.Short}} 59 {{else}}\ 60 {{printf "\t%s - %s" $cmdname .Cmd.Short}} 61 {{end}}\ 62 63 USAGE: 64 {{printf "\t%s" .Cmd.UseLine}} 65 {{ if not .Cmd.HasParent }}\ 66 67 VERSION: 68 {{printf "\t%s" .Version}} 69 {{end}}\ 70 {{if .Cmd.HasSubCommands}}\ 71 72 API VERSION: 73 {{printf "\t%s" .APIVersion}} 74 {{end}}\ 75 {{if .Cmd.HasSubCommands}}\ 76 77 78 COMMANDS: 79 {{range .SubCommands}}\ 80 {{ $cmdname := cmdName . $cmd }}\ 81 {{ if .Runnable }}\ 82 {{printf "\t%s\t%s" $cmdname .Short}} 83 {{end}}\ 84 {{end}}\ 85 {{end}}\ 86 {{ if .Cmd.Long }}\ 87 88 DESCRIPTION: 89 {{range $line := descToLines .Cmd.Long}}{{printf "\t%s" $line}} 90 {{end}}\ 91 {{end}}\ 92 {{if .Cmd.HasLocalFlags}}\ 93 94 OPTIONS: 95 {{.LocalFlags}}\ 96 {{end}}\ 97 {{if .Cmd.HasInheritedFlags}}\ 98 99 GLOBAL OPTIONS: 100 {{.GlobalFlags}}\ 101 {{end}} 102 `[1:] 103 104 commandUsageTemplate = template.Must(template.New("command_usage").Funcs(templFuncs).Parse(strings.Replace(commandUsage, "\\\n", "", -1))) 105 } 106 107 func etcdFlagUsages(flagSet *pflag.FlagSet) string { 108 x := new(bytes.Buffer) 109 110 flagSet.VisitAll(func(flag *pflag.Flag) { 111 if len(flag.Deprecated) > 0 { 112 return 113 } 114 var format string 115 if len(flag.Shorthand) > 0 { 116 format = " -%s, --%s" 117 } else { 118 format = " %s --%s" 119 } 120 if len(flag.NoOptDefVal) > 0 { 121 format = format + "[" 122 } 123 if flag.Value.Type() == "string" { 124 // put quotes on the value 125 format = format + "=%q" 126 } else { 127 format = format + "=%s" 128 } 129 if len(flag.NoOptDefVal) > 0 { 130 format = format + "]" 131 } 132 format = format + "\t%s\n" 133 shorthand := flag.Shorthand 134 fmt.Fprintf(x, format, shorthand, flag.Name, flag.DefValue, flag.Usage) 135 }) 136 137 return x.String() 138 } 139 140 func getSubCommands(cmd *cobra.Command) []*cobra.Command { 141 var subCommands []*cobra.Command 142 for _, subCmd := range cmd.Commands() { 143 subCommands = append(subCommands, subCmd) 144 subCommands = append(subCommands, getSubCommands(subCmd)...) 145 } 146 return subCommands 147 } 148 149 func usageFunc(cmd *cobra.Command) error { 150 subCommands := getSubCommands(cmd) 151 tabOut := getTabOutWithWriter(os.Stdout) 152 commandUsageTemplate.Execute(tabOut, struct { 153 Cmd *cobra.Command 154 LocalFlags string 155 GlobalFlags string 156 SubCommands []*cobra.Command 157 Version string 158 APIVersion string 159 }{ 160 cmd, 161 etcdFlagUsages(cmd.LocalFlags()), 162 etcdFlagUsages(cmd.InheritedFlags()), 163 subCommands, 164 version.Version, 165 version.APIVersion, 166 }) 167 tabOut.Flush() 168 return nil 169 } 170 171 func getTabOutWithWriter(writer io.Writer) *tabwriter.Writer { 172 aTabOut := new(tabwriter.Writer) 173 aTabOut.Init(writer, 0, 8, 1, '\t', 0) 174 return aTabOut 175 }