-
Notifications
You must be signed in to change notification settings - Fork 10
/
Copy pathnamedflagsets.go
169 lines (142 loc) · 5.07 KB
/
namedflagsets.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
package cobrautil
import (
"bytes"
"fmt"
"io"
"math/rand"
"strings"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)
// MarkFlagsHidden is a convenient way to mark flags as hidden in bulk.
func MarkFlagsHidden(flags *pflag.FlagSet, names ...string) error {
for _, name := range names {
if err := flags.MarkHidden(name); err != nil {
return fmt.Errorf("failed to mark flag as hidden: %w", err)
}
}
return nil
}
// NewNamedFlagSets creates a new NamedFlagSets and registers it with the
// provided command.
func NewNamedFlagSets(cmd *cobra.Command) *NamedFlagSets {
nfs := &NamedFlagSets{}
nfs.SetUsageTemplate(cmd)
return nfs
}
// SetUsageTemplate overrides the cobra usage template to include the
// named flags sections.
func (nfs *NamedFlagSets) SetUsageTemplate(cmd *cobra.Command) {
cobra.AddTemplateFunc(nfs.templateFuncName(), nfs.templateFunc)
cmd.SetUsageTemplate(nfs.usageTemplate())
}
// NamedFlagSets stores named flag sets in the order of calling FlagSet.
//
// This type is largely adapted from [k8s.io/component-base/cli/flag], but
// modified to be less brittle by integrating with cobra's templates rather
// than entirely overriding UsageFuncs and HelpFuncs.
type NamedFlagSets struct {
// Order is an ordered list of flag set names.
Order []string
// FlagSets stores the flag sets by name.
FlagSets map[string]*pflag.FlagSet
// NormalizeNameFunc is the normalize function which used to initialize
// FlagSets created by NamedFlagSets.
NormalizeNameFunc func(f *pflag.FlagSet, name string) pflag.NormalizedName
uniqueID int
}
// templateFuncName generates a random template name so that template, which
// has to be registered globally, isn't overridden by any other NamedFlagSets.
func (nfs *NamedFlagSets) templateFuncName() string {
if nfs.uniqueID == 0 {
nfs.uniqueID = rand.Int()
}
return fmt.Sprintf("namedFlagSets%d", nfs.uniqueID)
}
func hasVisibleFlags(flags *pflag.FlagSet) bool {
var found bool
flags.VisitAll(func(flag *pflag.Flag) {
if !flag.Hidden {
found = true
}
})
return found
}
func (nfs *NamedFlagSets) AddFlagSets(cmd *cobra.Command) {
for _, name := range nfs.Order {
cmd.Flags().AddFlagSet(nfs.FlagSet(name))
}
}
// FlagSet returns the flag set with the given name and adds it to the
// ordered name list if it is not in there yet.
func (nfs *NamedFlagSets) FlagSet(name string) *pflag.FlagSet {
if nfs.FlagSets == nil {
nfs.FlagSets = map[string]*pflag.FlagSet{}
}
if _, ok := nfs.FlagSets[name]; !ok {
flagSet := pflag.NewFlagSet(name, pflag.ExitOnError)
flagSet.SetNormalizeFunc(pflag.CommandLine.GetNormalizeFunc())
if nfs.NormalizeNameFunc != nil {
flagSet.SetNormalizeFunc(nfs.NormalizeNameFunc)
}
nfs.FlagSets[name] = flagSet
nfs.Order = append(nfs.Order, name)
}
return nfs.FlagSets[name]
}
// printSections prints the given names flag sets in sections, with the maximal
// given column number.
//
// If cols is zero, lines are not wrapped.
func (nfs *NamedFlagSets) printSections(w io.Writer, cols int) {
for _, name := range nfs.Order {
fs := nfs.FlagSets[name]
if !hasVisibleFlags(fs) {
continue
}
wideFS := pflag.NewFlagSet("", pflag.ExitOnError)
wideFS.AddFlagSet(fs)
var zzz string
if cols > 24 {
zzz = strings.Repeat("z", cols-24)
wideFS.Int(zzz, 0, strings.Repeat("z", cols-24))
}
var buf bytes.Buffer
fmt.Fprintf(&buf, "\n%s Flags:\n%s", name, wideFS.FlagUsagesWrapped(cols))
if cols > 24 {
i := strings.Index(buf.String(), zzz)
lines := strings.Split(buf.String()[:i], "\n")
fmt.Fprint(w, strings.Join(lines[:len(lines)-1], "\n"))
fmt.Fprintln(w)
} else {
fmt.Fprint(w, buf.String())
}
}
}
func (nfs *NamedFlagSets) templateFunc() string {
var b strings.Builder
nfs.printSections(&b, 0)
return b.String()
}
func (nfs *NamedFlagSets) usageTemplate() string {
return `Usage:{{if .Runnable}}
{{.UseLine}}{{end}}{{if .HasAvailableSubCommands}}
{{.CommandPath}} [command]{{end}}{{if gt (len .Aliases) 0}}
Aliases:
{{.NameAndAliases}}{{end}}{{if .HasExample}}
Examples:
{{.Example}}{{end}}{{if .HasAvailableSubCommands}}{{$cmds := .Commands}}{{if eq (len .Groups) 0}}
Available Commands:{{range $cmds}}{{if (or .IsAvailableCommand (eq .Name "help"))}}
{{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{else}}{{range $group := .Groups}}
{{.Title}}{{range $cmds}}{{if (and (eq .GroupID $group.ID) (or .IsAvailableCommand (eq .Name "help")))}}
{{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if not .AllChildCommandsHaveGroup}}
Additional Commands:{{range $cmds}}{{if (and (eq .GroupID "") (or .IsAvailableCommand (eq .Name "help")))}}
{{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}}
{{` + nfs.templateFuncName() + `}}
Global Flags:
{{.InheritedFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasHelpSubCommands}}
Additional help topics:{{range .Commands}}{{if .IsAdditionalHelpTopicCommand}}
{{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableSubCommands}}
Use "{{.CommandPath}} [command] --help" for more information about a command.{{end}}
`
}