help.go 3.9 KB

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