-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathenvflag.go
213 lines (187 loc) · 5.87 KB
/
envflag.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
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
package envflag
import (
"errors"
"flag"
"fmt"
"log"
"os"
"strings"
)
var (
debugEnabled = false
)
// EnvFlag represents a envflag object that contains several settings.
type EnvFlag struct {
flagSet *flag.FlagSet // the flagSet to process
minLength int // minLength defines min length of flag key, in order to support shortcut
envFlagDict map[string]string // envFlagDict is a user-defined env-flag map
showEnvKeyInUsage bool // show env key in usage
showEnvValInUsage bool // show env value in usage as default flag value
}
// ProcessFlagWithEnv parses flag from env.
// This function also add ENVIRONMENT VARIABLE to usage.
// NOTICE: flag.Parse() will not be called by this function.
func (ef EnvFlag) ProcessFlagWithEnv() error {
if ef.flagSet.Parsed() {
return errors.New("flag has already been parsed")
}
flagEnvMap := map[string]string{}
for k, v := range ef.envFlagDict {
flagEnvMap[v] = k
}
// Rewrite flag.Useage and show env key in usage
if ef.showEnvKeyInUsage {
ef.flagSet.VisitAll(func(f *flag.Flag) {
if len(f.Name) < ef.minLength {
return
}
envKey, ok := flagEnvMap[f.Name]
if !ok {
envKey = flagToEnv(f.Name)
}
envPrefix := fmt.Sprintf("[%s]", envKey)
if strings.HasPrefix(f.Usage, envPrefix) {
return
}
f.Usage = fmt.Sprintf("%s %s", envPrefix, f.Usage)
})
}
for _, envLine := range os.Environ() {
debug("Find a new line of environment variable, ", envLine)
envKV := strings.SplitN(envLine, "=", 2)
var key, value string
key = envKV[0]
if len(key) < ef.minLength {
continue
}
if len(envKV) > 1 {
value = envKV[1]
} else {
value = ""
}
var flagKey string
if userFlag, ok := ef.envFlagDict[key]; ok {
flagKey = userFlag
} else {
flagKey = envToFlag(key)
}
debug("ENV ", key, " is converted to ", flagKey)
f := ef.flagSet.Lookup(flagKey)
if f == nil {
debug(flagKey, " is not defined in flag, skip!")
continue
}
if ef.showEnvValInUsage {
// use env value as default value (in usage)
f.DefValue = value
}
debug("Set [", flagKey, ",", value, "] to flag")
if err := ef.flagSet.Set(flagKey, value); err != nil {
return fmt.Errorf("error when set [%s,%s] into flag\n", flagKey, value)
}
}
return nil
}
// ProcessFlagWithEnv parses flag from env.
// This function also add ENVIRONMENT VARIABLE to usage.
// NOTICE: flag.Parse() will not be called by this function.
func ProcessFlagWithEnv() error {
return std.ProcessFlagWithEnv()
}
// Parse parses flag definitions from env and the argument list.
// Value from env can be overrided by the argument list.
// This function also add ENVIRONMENT VARIABLE to usage.
// It is extremely recommended to call this function in main()
// after all flags are defined and before flags are accessed by the program.
// NOTICE: flag.Parse() will be called by this function.
func (ef EnvFlag) Parse(arguments []string) error {
if err := ef.ProcessFlagWithEnv(); err != nil {
return err
}
return ef.flagSet.Parse(arguments)
}
// Parse parses the command-line flags from env and os.Args[1:].
// Value from env can be overrided by os.Args[1:].
// This function also add ENVIRONMENT VARIABLE to usage.
// It is extremely recommended to call this function in main()
// after all flags are defined and before flags are accessed by the program.
// NOTICE: flag.Parse() will be called by this function.
func Parse() error {
return std.Parse(os.Args[1:])
}
// SetMinLength sets the min length.
// EnvFlag only parses the environment variables that is longer than min length
// and modify usage that is longer than min length.
func (ef *EnvFlag) SetMinLength(v int) {
ef.minLength = v
}
// SetMinLength sets the min length for standard envflag.
// EnvFlag only parses the environment variables that is longer than min length
// and modify usage that is longer than min length.
func SetMinLength(v int) {
std.SetMinLength(v)
}
// SetEnvFlagDict sets a user-defined env-flag map.
func (ef *EnvFlag) SetEnvFlagDict(v map[string]string) {
ef.envFlagDict = v
}
// SetEnvFlagDict sets a user-defined env-flag map for standard envflag.
func SetEnvFlagDict(v map[string]string) {
std.SetEnvFlagDict(v)
}
// SetShowEnvKeyInUsage sets whether show env key in usage.
func (ef *EnvFlag) SetShowEnvKeyInUsage(v bool) {
ef.showEnvKeyInUsage = v
}
// SetShowEnvKeyInUsage sets whether show env key in usage for standard envflag.
func SetShowEnvKeyInUsage(v bool) {
std.showEnvKeyInUsage = v
}
// SetShowEnvValInUsage sets whether show env value in usage.
// This is useful for confirming the environment variables.
func (ef *EnvFlag) SetShowEnvValInUsage(v bool) {
ef.showEnvValInUsage = v
}
// SetShowEnvValInUsage sets whether show env value in usage for standard envflag.
// This is useful for confirming the environment variables.
func SetShowEnvValInUsage(v bool) {
std.showEnvValInUsage = v
}
var std = NewEnvFlag(flag.CommandLine, 3, map[string]string{}, true, true)
// NewEnvFlag returns a new EnvFlag.
func NewEnvFlag(
flagSet *flag.FlagSet,
minLength int,
envFlagDict map[string]string,
showEnvKeyInUsage bool,
showEnvValInUsage bool) *EnvFlag {
return &EnvFlag{
flagSet: flagSet,
minLength: minLength,
envFlagDict: envFlagDict,
showEnvKeyInUsage: showEnvKeyInUsage,
showEnvValInUsage: showEnvValInUsage,
}
}
// DebugEnabled returns whether the debug is enabled or not
func DebugEnabled() bool {
return debugEnabled
}
// SetDebugEnabled enables debug info
func SetDebugEnabled(enabled bool) {
debugEnabled = enabled
}
// envToFlag converts THIS_FORMAT to this-format
func envToFlag(e string) string {
return strings.Replace(strings.ToLower(e), "_", "-", -1)
}
// flagToEnv converts this-format to THIS_FORMAT
func flagToEnv(f string) string {
return strings.Replace(strings.ToUpper(f), "-", "_", -1)
}
func debug(v ...interface{}) {
if !debugEnabled {
return
}
log.Println(v...)
}