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