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 17package ctlv3 18 19import ( 20 "bytes" 21 "fmt" 22 "io" 23 "os" 24 "strings" 25 "text/tabwriter" 26 "text/template" 27 28 "github.com/coreos/etcd/version" 29 "github.com/spf13/cobra" 30 "github.com/spf13/pflag" 31) 32 33var ( 34 commandUsageTemplate *template.Template 35 templFuncs = template.FuncMap{ 36 "descToLines": func(s string) []string { 37 // trim leading/trailing whitespace and split into slice of lines 38 return strings.Split(strings.Trim(s, "\n\t "), "\n") 39 }, 40 "cmdName": func(cmd *cobra.Command, startCmd *cobra.Command) string { 41 parts := []string{cmd.Name()} 42 for cmd.HasParent() && cmd.Parent().Name() != startCmd.Name() { 43 cmd = cmd.Parent() 44 parts = append([]string{cmd.Name()}, parts...) 45 } 46 return strings.Join(parts, " ") 47 }, 48 } 49) 50 51func init() { 52 commandUsage := ` 53{{ $cmd := .Cmd }}\ 54{{ $cmdname := cmdName .Cmd .Cmd.Root }}\ 55NAME: 56{{ if not .Cmd.HasParent }}\ 57{{printf "\t%s - %s" .Cmd.Name .Cmd.Short}} 58{{else}}\ 59{{printf "\t%s - %s" $cmdname .Cmd.Short}} 60{{end}}\ 61 62USAGE: 63{{printf "\t%s" .Cmd.UseLine}} 64{{ if not .Cmd.HasParent }}\ 65 66VERSION: 67{{printf "\t%s" .Version}} 68{{end}}\ 69{{if .Cmd.HasSubCommands}}\ 70 71API VERSION: 72{{printf "\t%s" .APIVersion}} 73{{end}}\ 74{{if .Cmd.HasSubCommands}}\ 75 76 77COMMANDS: 78{{range .SubCommands}}\ 79{{ $cmdname := cmdName . $cmd }}\ 80{{ if .Runnable }}\ 81{{printf "\t%s\t%s" $cmdname .Short}} 82{{end}}\ 83{{end}}\ 84{{end}}\ 85{{ if .Cmd.Long }}\ 86 87DESCRIPTION: 88{{range $line := descToLines .Cmd.Long}}{{printf "\t%s" $line}} 89{{end}}\ 90{{end}}\ 91{{if .Cmd.HasLocalFlags}}\ 92 93OPTIONS: 94{{.LocalFlags}}\ 95{{end}}\ 96{{if .Cmd.HasInheritedFlags}}\ 97 98GLOBAL OPTIONS: 99{{.GlobalFlags}}\ 100{{end}} 101`[1:] 102 103 commandUsageTemplate = template.Must(template.New("command_usage").Funcs(templFuncs).Parse(strings.Replace(commandUsage, "\\\n", "", -1))) 104} 105 106func etcdFlagUsages(flagSet *pflag.FlagSet) string { 107 x := new(bytes.Buffer) 108 109 flagSet.VisitAll(func(flag *pflag.Flag) { 110 if len(flag.Deprecated) > 0 { 111 return 112 } 113 var format string 114 if len(flag.Shorthand) > 0 { 115 format = " -%s, --%s" 116 } else { 117 format = " %s --%s" 118 } 119 if len(flag.NoOptDefVal) > 0 { 120 format = format + "[" 121 } 122 if flag.Value.Type() == "string" { 123 // put quotes on the value 124 format = format + "=%q" 125 } else { 126 format = format + "=%s" 127 } 128 if len(flag.NoOptDefVal) > 0 { 129 format = format + "]" 130 } 131 format = format + "\t%s\n" 132 shorthand := flag.Shorthand 133 fmt.Fprintf(x, format, shorthand, flag.Name, flag.DefValue, flag.Usage) 134 }) 135 136 return x.String() 137} 138 139func getSubCommands(cmd *cobra.Command) []*cobra.Command { 140 var subCommands []*cobra.Command 141 for _, subCmd := range cmd.Commands() { 142 subCommands = append(subCommands, subCmd) 143 subCommands = append(subCommands, getSubCommands(subCmd)...) 144 } 145 return subCommands 146} 147 148func usageFunc(cmd *cobra.Command) error { 149 subCommands := getSubCommands(cmd) 150 tabOut := getTabOutWithWriter(os.Stdout) 151 commandUsageTemplate.Execute(tabOut, struct { 152 Cmd *cobra.Command 153 LocalFlags string 154 GlobalFlags string 155 SubCommands []*cobra.Command 156 Version string 157 APIVersion string 158 }{ 159 cmd, 160 etcdFlagUsages(cmd.LocalFlags()), 161 etcdFlagUsages(cmd.InheritedFlags()), 162 subCommands, 163 version.Version, 164 version.APIVersion, 165 }) 166 tabOut.Flush() 167 return nil 168} 169 170func getTabOutWithWriter(writer io.Writer) *tabwriter.Writer { 171 aTabOut := new(tabwriter.Writer) 172 aTabOut.Init(writer, 0, 8, 1, '\t', 0) 173 return aTabOut 174} 175