-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathflag.go
More file actions
225 lines (193 loc) · 6.22 KB
/
flag.go
File metadata and controls
225 lines (193 loc) · 6.22 KB
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
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
package warg
import (
"log"
"sort"
"go.bbkane.com/warg/completion"
"go.bbkane.com/warg/value"
)
// FlagOpt customizes a Flag on creation
type FlagOpt func(*Flag)
// NewFlag creates a NewFlag with options!
func NewFlag(helpShort string, empty value.EmptyConstructor, opts ...FlagOpt) Flag {
flag := Flag{
Alias: "",
Completions: defaultFlagCompletions,
ConfigPath: "",
EmptyValueConstructor: empty,
EnvVars: nil,
Group: "",
HelpShort: helpShort,
Required: false,
UnsetSentinel: nil,
}
for _, opt := range opts {
opt(&flag)
}
return flag
}
// Alias is an alternative name for a flag, usually shorter :)
func Alias(alias string) FlagOpt {
return func(f *Flag) {
f.Alias = alias
}
}
// ConfigPath adds a configpath to a flag
func ConfigPath(path string) FlagOpt {
return func(f *Flag) {
f.ConfigPath = path
}
}
func defaultFlagCompletions(cmdCtx CmdContext) (*completion.Candidates, error) {
choices := cmdCtx.ParseState.FlagValues[cmdCtx.ParseState.CurrentFlagName].Choices()
if len(choices) > 0 {
candidates := &completion.Candidates{
Type: completion.Type_Values,
Values: []completion.Candidate{},
}
// pr.FlagValues is always filled with at least the empty values
for _, name := range choices {
candidates.Values = append(candidates.Values, completion.Candidate{
Name: name,
Description: "",
})
}
return candidates, nil
}
// special case: bools can only be true or false, so let's be helpful and suggest those
if _, ok := cmdCtx.ParseState.FlagValues[cmdCtx.ParseState.CurrentFlagName].Get().(bool); ok {
return &completion.Candidates{
Type: completion.Type_Values,
Values: []completion.Candidate{
{Name: "true", Description: ""},
{Name: "false", Description: ""},
},
}, nil
}
// default
return &completion.Candidates{
Type: completion.Type_DirectoriesFiles,
Values: nil,
}, nil
}
func FlagCompletions(CompletionsFunc CompletionsFunc) FlagOpt {
return func(flag *Flag) {
flag.Completions = CompletionsFunc
}
}
// EnvVars adds a list of environmental variables to search through to update this flag. The first one that exists will be used to update the flag. Further existing envvars will be ignored.
func EnvVars(name ...string) FlagOpt {
return func(f *Flag) {
f.EnvVars = name
}
}
// Required means the user MUST fill this flag
func Required() FlagOpt {
return func(f *Flag) {
f.Required = true
}
}
// UnsetSentinel is a bit of an advanced feature meant to allow overriding a
// default, config, or environmental value with a command line flag.
// When UnsetSentinel is passed as a flag value, Value is reset and SetBy is set to "".
// It it recommended to set `name` to "UNSET" for consistency among warg apps.
// Scalar example:
//
// app --flag UNSET // undoes anything that sets --flag
//
// Slice example:
//
// app --flag a --flag b --flag UNSET --flag c --flag d // ends up with []string{"c", "d"}
func UnsetSentinel(name string) FlagOpt {
return func(f *Flag) {
f.UnsetSentinel = &name
}
}
// FlagGroup sets the group name for a flag.
// Flags with the same group are displayed together in help output.
func FlagGroup(group string) FlagOpt {
return func(f *Flag) {
f.Group = group
}
}
// FlagMap holds flags - used by Commands and Sections
type FlagMap map[string]Flag
func (fm *FlagMap) SortedNames() []string {
keys := make([]string, 0, len(*fm))
for k := range *fm {
keys = append(keys, k)
}
sort.Slice(keys, func(i, j int) bool {
return string(keys[i]) < string(keys[j])
})
return keys
}
// FlagNameGroup is a named group of flag names, used for grouped help output.
type FlagNameGroup struct {
// Name is the group name. Empty string means ungrouped (displayed first).
Name string
// FlagNames are the sorted flag names in this group.
FlagNames []string
}
// groupedNames returns flag names organized by Group, with ungrouped flags first,
// then groups in alphabetical order. Within each group, flags are sorted alphabetically.
func (fm *FlagMap) groupedNames() []FlagNameGroup {
groups := make(map[string][]string)
for name, flag := range *fm {
groups[flag.Group] = append(groups[flag.Group], name)
}
for _, names := range groups {
sort.Strings(names)
}
groupNames := make([]string, 0, len(groups))
for g := range groups {
if g != "" {
groupNames = append(groupNames, g)
}
}
sort.Strings(groupNames)
var result []FlagNameGroup
if names, ok := groups[""]; ok {
result = append(result, FlagNameGroup{Name: "", FlagNames: names})
}
for _, g := range groupNames {
result = append(result, FlagNameGroup{Name: g, FlagNames: groups[g]})
}
return result
}
// AddFlag adds a new flag and panics if it already exists
func (fm FlagMap) AddFlag(name string, value Flag) {
if _, alreadyThere := (fm)[name]; !alreadyThere {
(fm)[name] = value
} else {
log.Panicf("flag already exists: %#v\n", name)
}
}
// AddFlags adds another FlagMap to this one and and panics if a flag name already exists
func (fm FlagMap) AddFlags(flagMap FlagMap) {
for name, value := range flagMap {
fm.AddFlag(name, value)
}
}
type Flag struct {
// Alias is an alternative name for a flag, usually shorter :)
Alias string
// Completions is a function that returns a list of completion candidates for this flag.
// Note that some flags in the cli.Context Flags map may not be set, even if they're required.
// TODO: get a comprehensive list of restrictions on the context.
Completions CompletionsFunc
// ConfigPath is the path from the config to the value the flag updates
ConfigPath string
// EmptyConstructor tells flag how to make a value
EmptyValueConstructor value.EmptyConstructor
// Envvars holds a list of environment variables to update this flag. Only the first one that exists will be used.
EnvVars []string
// Group is an optional group name for organizing flags in help output.
// Flags with an empty group are printed first, then groups are printed in alphabetical order.
Group string
// HelpShort is a message for the user on how to use this flag
HelpShort string
// Required means the user MUST fill this flag
Required bool
// When UnsetSentinal is passed as a flag value, Value is reset and SetBy is set to ""
UnsetSentinel *string
}