-
Notifications
You must be signed in to change notification settings - Fork 0
/
flagset.go
158 lines (137 loc) · 4.04 KB
/
flagset.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
package clix
import (
"errors"
"fmt"
"strings"
"github.com/urfave/cli/v2"
)
// FlagSet represents ...
type FlagSet map[string]struct{}
// NewFlagSet returns a FlagSet.
func NewFlagSet() FlagSet {
return make(FlagSet)
}
// Init initialize fs using c.LocalFlagNames().
func (fs FlagSet) Init(c *cli.Context) error {
for _, name := range c.LocalFlagNames() {
fs[name] = struct{}{}
}
return nil
}
// IsSetArgs returns true if flag is specified in arguments
// or false otherwise.
func (fs FlagSet) IsSetArgs(flag cli.Flag) bool {
for _, name := range flag.Names() {
if _, ok := fs[name]; ok {
return true
}
}
return false
}
// IsSet returns true if flag is specified or false otherwise.
// `specified` includes following casese.
// - specified by arguments
// - specified by EnvVars
// - specified by FilePath
func (fs FlagSet) IsSet(flag cli.Flag) bool {
// flag.IsSet() returns true in following cases.
// - specified by EnvVars
// - specified by FilePath
// fs.IsSetArgs(flag) returns true if it exists in arguments.
return flag.IsSet() || fs.IsSetArgs(flag)
}
var (
// ErrExclusiveFlagsInArgs represents an error
// where more than one exclusive flags are set
// at the same time.
ErrExclusiveFlags = errors.New("more than one flags are set")
// ErrExclusiveFlagsInArgs represents an error
// where more than one exclusive flags are set in arguments
// at the same time.
ErrExclusiveFlagsInArgs = fmt.Errorf("%w in args", ErrExclusiveFlags)
// ErrExclusiveFlagsInEnvs represents an error
// where more than one exclusive flags are set in environment variables
// at the same time.
ErrExclusiveFlagsInEnvs = fmt.Errorf("%w in envs", ErrExclusiveFlags)
)
// Exclusive returns the flag only if it is set exclusively or nil if none is set,
// returns err!=nil if and only if multiple flags are set at the same time.
//
// At first command line arguments are searched.
// Next the environment variables (include FilePath) are searched.
// At most one flag can be specified at the same time,
// otherwise err!=nil is returned.
func (fs FlagSet) Exclusive(flag ...cli.Flag) (cli.Flag, error) {
p := newPicker(len(flag))
// fs.IsSetArgs(flag) returns true if it exists in arguments.
n := p.pick(flag, fs.IsSetArgs)
switch {
case n == 1:
f := p.found[0]
return f, nil
case n > 1:
return nil, fmt.Errorf("%w ("+joinNames(p.found)+")", ErrExclusiveFlagsInArgs)
}
p.reset()
// f.IsSet() returns true in following cases.
// - specified by EnvVars
// - specified by FilePath
n = p.pick(flag, func(f cli.Flag) bool { return f.IsSet() })
switch {
case n == 1:
f := p.found[0]
return f, nil
case n > 1:
return nil, fmt.Errorf("%w ("+joinNames(p.found)+")", ErrExclusiveFlagsInEnvs)
}
return nil, nil
}
type picker struct {
found []cli.Flag
}
func newPicker(hint int) *picker {
return &picker{found: make([]cli.Flag, 0, hint)}
}
func (p *picker) reset() {
p.found = p.found[:0]
}
func (p *picker) pick(flags []cli.Flag, fn func(cli.Flag) bool) int {
for _, f := range flags {
if fn(f) {
p.found = append(p.found, f)
}
}
return len(p.found)
}
func joinNames(flags []cli.Flag) string {
names := make([]string, len(flags))
for i, f := range flags {
names[i] = "-" + f.Names()[0]
}
return strings.Join(names, ",")
}
// Select calls the callback function for exclusive flag that is set,
// returns the result of the callback.
func (fs FlagSet) Select(flags []cli.Flag, cb map[cli.Flag]func() error) error {
flag, err := fs.Exclusive(flags...)
if err == nil {
if fn := cb[flag]; fn != nil {
err = fn()
}
}
return err
}
// NewExclusiveFlags returns ExclusiveFlags.
func (fs FlagSet) NewExclusiveFlags(flag ...cli.Flag) *ExclusiveFlags {
return &ExclusiveFlags{FlagSet: fs, Flags: flag}
}
// ExclusiveFlags is pair of FlagSet and Flags.
type ExclusiveFlags struct {
FlagSet FlagSet
Flags []cli.Flag
}
// Select calls the callback function of the exclusive flag that is set,
// returns the result of the callback.
func (ef ExclusiveFlags) Select(cb map[cli.Flag]func() error) error {
return ef.FlagSet.Select(ef.Flags, cb)
}