-
Notifications
You must be signed in to change notification settings - Fork 0
/
envvar.go
227 lines (181 loc) · 5.64 KB
/
envvar.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
214
215
216
217
218
219
220
221
222
223
224
225
226
227
package env
import (
"fmt"
"io"
"os"
"sort"
"strings"
)
// ErrorHandling defines how to handle env var parsing errors.
type ErrorHandling int
const (
// ContinueOnError will return an err from Parse() if an error is found
ContinueOnError ErrorHandling = iota
// ExitOnError will call os.Exit(2) if an error is found
ExitOnError
// PanicOnError will panic() if an error is found
PanicOnError
)
// EnvVarSet is a set of defined environment variables.
type EnvVarSet struct {
// SortVars is used to indicate, if user wants to have sorted environment variables in
// help/usage messages.
SortVars bool
parsed bool
vars map[NormalizedName]*EnvVar
orderedVars []*EnvVar
sortedVars []*EnvVar
errorHandling ErrorHandling
output io.Writer // nil means stderr; use out() accessor
normalizeNameFunc NormalizeFunc
}
// EnvVar represents the state of an environment variable.
type EnvVar struct {
// Name of the environment variable
Name string
// Usage message
Usage string
// Value as set
Value Value
// DefaultValue is shown in the usage message
DefaultValue string
}
// NewEnvVarSet returns a new, empty environment variable set with the specified
// error handling property and SortFlags set to true.
func NewEnvVarSet(errorHandling ErrorHandling) *EnvVarSet {
return &EnvVarSet{
SortVars: true,
errorHandling: errorHandling,
}
}
// Var defines an environment variable with the specified name and usage string.
// The type and value of the variable are represented by the first argument,
// of type Value, which typically holds a user-defined implementation of Value.
// For instance, the caller could create an environment variable
// that turns a comma-separated string into a slice of strings by giving the slice the methods of Value;
// in particular, Set would decompose the comma-separated string into the slice.
func (s *EnvVarSet) Var(value Value, name string, usage string) {
s.VarE(value, name, usage)
}
// VarE is like Var, but returns the created EnvVar.
func (s *EnvVarSet) VarE(value Value, name string, usage string) *EnvVar {
envVar := &EnvVar{
Name: name,
Usage: usage,
Value: value,
DefaultValue: value.String(),
}
s.AddEnvVar(envVar)
return envVar
}
// AddEnvVar will add the environment variable to the EnvVarSet.
func (s *EnvVarSet) AddEnvVar(envVar *EnvVar) {
if s.vars == nil {
s.vars = make(map[NormalizedName]*EnvVar)
}
name := s.normalizeVarName(envVar.Name)
_, alreadyThere := s.vars[name]
if alreadyThere {
msg := fmt.Sprintf("%s environment variable redefined: %s", name, envVar.Name)
fmt.Fprintln(s.out(), msg)
panic(msg) // Happens only if environment variables are declared with identical names
}
s.vars[name] = envVar
s.orderedVars = append(s.orderedVars, envVar)
}
func (s *EnvVarSet) out() io.Writer {
if s.output == nil {
return os.Stderr
}
return s.output
}
// SetOutput sets the destination for usage and error messages.
// If output is nil, os.Stderr is used.
func (s *EnvVarSet) SetOutput(output io.Writer) {
s.output = output
}
// VisitAll visits the environment variables in lexicographical order or
// in primordial order if f.SortVars is false, calling fn for each.
// It visits all variables, even those not set.
func (s *EnvVarSet) VisitAll(fn func(*EnvVar)) {
if len(s.vars) == 0 {
return
}
var envVars []*EnvVar
if s.SortVars {
if len(s.vars) != len(s.sortedVars) {
s.sortedVars = sortVars(s.vars)
}
envVars = s.sortedVars
} else {
envVars = s.orderedVars
}
for _, envVar := range envVars {
fn(envVar)
}
}
// sortVars returns the environment variables as a slice in lexicographical sorted order.
func sortVars(envVars map[NormalizedName]*EnvVar) []*EnvVar {
list := make(sort.StringSlice, len(envVars))
i := 0
for k := range envVars {
list[i] = string(k)
i++
}
list.Sort()
result := make([]*EnvVar, len(list))
for i, name := range list {
result[i] = envVars[NormalizedName(name)]
}
return result
}
// HasEnvVars returns a bool to indicate if the EnvVarSet has any environment variables defined.
func (s *EnvVarSet) HasEnvVars() bool {
return len(s.vars) > 0
}
// Lookup returns the EnvVar structure of the named environment variable, returning nil if none exists.
func (s *EnvVarSet) Lookup(name string) *EnvVar {
return s.vars[s.normalizeVarName(name)]
}
// Parse parses environment variables according to the definitions in the EnvVarSet.
// Must be called after all variables in the EnvVarSet
// are defined and before variables are accessed by the program.
func (s *EnvVarSet) Parse(environment map[string]string) error {
s.parsed = true
for name, value := range environment {
if ev, ok := s.vars[NormalizedName(name)]; ok {
err := ev.Value.Set(value)
if err != nil {
switch s.errorHandling {
case ContinueOnError:
return err
case ExitOnError:
fmt.Fprintln(s.out(), err)
os.Exit(2)
case PanicOnError:
panic(err)
}
}
}
}
return nil
}
// ParseEnviron accepts a list of environment variables in the "key=value" format
// returned by os.Environ(), transforms it into a string map and calls Parse.
func (s *EnvVarSet) ParseEnviron(environ []string) error {
env := map[string]string{}
for _, value := range environ {
v := strings.SplitN(value, "=", 2)
env[v[0]] = v[1]
}
return s.Parse(env)
}
// Parsed reports whether Parse has been called on EnvVarSet.
func (s *EnvVarSet) Parsed() bool {
return s.parsed
}
// Init sets error handling property for an env var set.
// By default, the zero EnvVarSet uses the ContinueOnError error handling policy.
func (s *EnvVarSet) Init(errorHandling ErrorHandling) {
s.errorHandling = errorHandling
}