diff options
author | Ben Kochie <superq@gmail.com> | 2018-10-31 12:22:23 +0300 |
---|---|---|
committer | Ben Kochie <superq@gmail.com> | 2018-10-31 12:22:23 +0300 |
commit | 54e738ec3d5596b9e8d432f2cbd75e8c00c1200b (patch) | |
tree | 60f2d8fd8e3c70c33c1ef837308becf577feaf66 | |
parent | 5a9e3f5462f28bca3acf970dd3b845fdcbf05d62 (diff) |
Update vendor github.com/kelseyhightower/envconfig@v1
-rw-r--r-- | vendor/github.com/kelseyhightower/envconfig/README.md | 25 | ||||
-rw-r--r-- | vendor/github.com/kelseyhightower/envconfig/envconfig.go | 110 | ||||
-rw-r--r-- | vendor/github.com/kelseyhightower/envconfig/usage.go | 158 | ||||
-rw-r--r-- | vendor/vendor.json | 8 |
4 files changed, 260 insertions, 41 deletions
diff --git a/vendor/github.com/kelseyhightower/envconfig/README.md b/vendor/github.com/kelseyhightower/envconfig/README.md index 09de74b42..b6c65a8a3 100644 --- a/vendor/github.com/kelseyhightower/envconfig/README.md +++ b/vendor/github.com/kelseyhightower/envconfig/README.md @@ -21,6 +21,7 @@ export MYAPP_USER=Kelsey export MYAPP_RATE="0.5" export MYAPP_TIMEOUT="3m" export MYAPP_USERS="rob,ken,robert" +export MYAPP_COLORCODES="red:1,green:2,blue:3" ``` Write some code: @@ -37,12 +38,13 @@ import ( ) type Specification struct { - Debug bool - Port int - User string - Users []string - Rate float32 - Timeout time.Duration + Debug bool + Port int + User string + Users []string + Rate float32 + Timeout time.Duration + ColorCodes map[string]int } func main() { @@ -61,6 +63,11 @@ func main() { for _, u := range s.Users { fmt.Printf(" %s\n", u) } + + fmt.Println("Color codes:") + for k, v := range s.ColorCodes { + fmt.Printf(" %s: %d\n", k, v) + } } ``` @@ -76,6 +83,10 @@ Users: rob ken robert +Color codes: + red: 1 + green: 2 + blue: 3 ``` ## Struct Tag Support @@ -145,6 +156,8 @@ envconfig supports supports these struct field types: * int8, int16, int32, int64 * bool * float32, float64 + * slices of any supported type + * maps (keys and values of any supported type) * [encoding.TextUnmarshaler](https://golang.org/pkg/encoding/#TextUnmarshaler) Embedded structs using these fields are also supported. diff --git a/vendor/github.com/kelseyhightower/envconfig/envconfig.go b/vendor/github.com/kelseyhightower/envconfig/envconfig.go index 38e449ebb..892d74699 100644 --- a/vendor/github.com/kelseyhightower/envconfig/envconfig.go +++ b/vendor/github.com/kelseyhightower/envconfig/envconfig.go @@ -44,21 +44,31 @@ func (e *ParseError) Error() string { return fmt.Sprintf("envconfig.Process: assigning %[1]s to %[2]s: converting '%[3]s' to type %[4]s. details: %[5]s", e.KeyName, e.FieldName, e.Value, e.TypeName, e.Err) } -// Process populates the specified struct based on environment variables -func Process(prefix string, spec interface{}) error { - // For splitting camelCased words - expr := regexp.MustCompile("([^A-Z]+|[A-Z][^A-Z]+|[A-Z]+)") +// varInfo maintains information about the configuration variable +type varInfo struct { + Name string + Alt string + Key string + Field reflect.Value + Tags reflect.StructTag +} +// GatherInfo gathers information about the specified struct +func gatherInfo(prefix string, spec interface{}) ([]varInfo, error) { + expr := regexp.MustCompile("([^A-Z]+|[A-Z][^A-Z]+|[A-Z]+)") s := reflect.ValueOf(spec) if s.Kind() != reflect.Ptr { - return ErrInvalidSpecification + return nil, ErrInvalidSpecification } s = s.Elem() if s.Kind() != reflect.Struct { - return ErrInvalidSpecification + return nil, ErrInvalidSpecification } typeOfSpec := s.Type() + + // over allocate an info array, we will extend if needed later + infos := make([]varInfo, 0, s.NumField()) for i := 0; i < s.NumField(); i++ { f := s.Field(i) ftype := typeOfSpec.Field(i) @@ -78,8 +88,16 @@ func Process(prefix string, spec interface{}) error { f = f.Elem() } + // Capture information about the config variable + info := varInfo{ + Name: ftype.Name, + Field: f, + Tags: ftype.Tag, + Alt: strings.ToUpper(ftype.Tag.Get("envconfig")), + } + // Default to the field name as the env var name (will be upcased) - key := ftype.Name + info.Key = info.Name // Best effort to un-pick camel casing as separate words if ftype.Tag.Get("split_words") == "true" { @@ -90,74 +108,81 @@ func Process(prefix string, spec interface{}) error { name = append(name, words[0]) } - key = strings.Join(name, "_") + info.Key = strings.Join(name, "_") } } - - alt := ftype.Tag.Get("envconfig") - if alt != "" { - key = alt + if info.Alt != "" { + info.Key = info.Alt } - if prefix != "" { - key = fmt.Sprintf("%s_%s", prefix, key) + info.Key = fmt.Sprintf("%s_%s", prefix, info.Key) } - - key = strings.ToUpper(key) + info.Key = strings.ToUpper(info.Key) + infos = append(infos, info) if f.Kind() == reflect.Struct { // honor Decode if present if decoderFrom(f) == nil && setterFrom(f) == nil && textUnmarshaler(f) == nil { innerPrefix := prefix if !ftype.Anonymous { - innerPrefix = key + innerPrefix = info.Key } embeddedPtr := f.Addr().Interface() - if err := Process(innerPrefix, embeddedPtr); err != nil { - return err + embeddedInfos, err := gatherInfo(innerPrefix, embeddedPtr) + if err != nil { + return nil, err } - f.Set(reflect.ValueOf(embeddedPtr).Elem()) + infos = append(infos[:len(infos)-1], embeddedInfos...) continue } } + } + return infos, nil +} + +// Process populates the specified struct based on environment variables +func Process(prefix string, spec interface{}) error { + infos, err := gatherInfo(prefix, spec) + + for _, info := range infos { // `os.Getenv` cannot differentiate between an explicitly set empty value // and an unset value. `os.LookupEnv` is preferred to `syscall.Getenv`, // but it is only available in go1.5 or newer. We're using Go build tags // here to use os.LookupEnv for >=go1.5 - value, ok := lookupEnv(key) - if !ok && alt != "" { - key := strings.ToUpper(alt) - value, ok = lookupEnv(key) + value, ok := lookupEnv(info.Key) + if !ok && info.Alt != "" { + value, ok = lookupEnv(info.Alt) } - def := ftype.Tag.Get("default") + def := info.Tags.Get("default") if def != "" && !ok { value = def } - req := ftype.Tag.Get("required") + req := info.Tags.Get("required") if !ok && def == "" { if req == "true" { - return fmt.Errorf("required key %s missing value", key) + return fmt.Errorf("required key %s missing value", info.Key) } continue } - err := processField(value, f) + err := processField(value, info.Field) if err != nil { return &ParseError{ - KeyName: key, - FieldName: ftype.Name, - TypeName: f.Type().String(), + KeyName: info.Key, + FieldName: info.Name, + TypeName: info.Field.Type().String(), Value: value, Err: err, } } } - return nil + + return err } // MustProcess is the same as Process but panics if an error occurs @@ -240,6 +265,27 @@ func processField(value string, field reflect.Value) error { } } field.Set(sl) + case reflect.Map: + pairs := strings.Split(value, ",") + mp := reflect.MakeMap(typ) + for _, pair := range pairs { + kvpair := strings.Split(pair, ":") + if len(kvpair) != 2 { + return fmt.Errorf("invalid map item: %q", pair) + } + k := reflect.New(typ.Key()).Elem() + err := processField(kvpair[0], k) + if err != nil { + return err + } + v := reflect.New(typ.Elem()).Elem() + err = processField(kvpair[1], v) + if err != nil { + return err + } + mp.SetMapIndex(k, v) + } + field.Set(mp) } return nil diff --git a/vendor/github.com/kelseyhightower/envconfig/usage.go b/vendor/github.com/kelseyhightower/envconfig/usage.go new file mode 100644 index 000000000..184635380 --- /dev/null +++ b/vendor/github.com/kelseyhightower/envconfig/usage.go @@ -0,0 +1,158 @@ +// Copyright (c) 2016 Kelsey Hightower and others. All rights reserved. +// Use of this source code is governed by the MIT License that can be found in +// the LICENSE file. + +package envconfig + +import ( + "encoding" + "fmt" + "io" + "os" + "reflect" + "strconv" + "strings" + "text/tabwriter" + "text/template" +) + +const ( + // DefaultListFormat constant to use to display usage in a list format + DefaultListFormat = `This application is configured via the environment. The following environment +variables can be used: +{{range .}} +{{usage_key .}} + [description] {{usage_description .}} + [type] {{usage_type .}} + [default] {{usage_default .}} + [required] {{usage_required .}}{{end}} +` + // DefaultTableFormat constant to use to display usage in a tabluar format + DefaultTableFormat = `This application is configured via the environment. The following environment +variables can be used: + +KEY TYPE DEFAULT REQUIRED DESCRIPTION +{{range .}}{{usage_key .}} {{usage_type .}} {{usage_default .}} {{usage_required .}} {{usage_description .}} +{{end}}` +) + +var ( + decoderType = reflect.TypeOf((*Decoder)(nil)).Elem() + setterType = reflect.TypeOf((*Setter)(nil)).Elem() + unmarshalerType = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem() +) + +func implementsInterface(t reflect.Type) bool { + return t.Implements(decoderType) || + reflect.PtrTo(t).Implements(decoderType) || + t.Implements(setterType) || + reflect.PtrTo(t).Implements(setterType) || + t.Implements(unmarshalerType) || + reflect.PtrTo(t).Implements(unmarshalerType) +} + +// toTypeDescription converts Go types into a human readable description +func toTypeDescription(t reflect.Type) string { + switch t.Kind() { + case reflect.Array, reflect.Slice: + return fmt.Sprintf("Comma-separated list of %s", toTypeDescription(t.Elem())) + case reflect.Map: + return fmt.Sprintf( + "Comma-separated list of %s:%s pairs", + toTypeDescription(t.Key()), + toTypeDescription(t.Elem()), + ) + case reflect.Ptr: + return toTypeDescription(t.Elem()) + case reflect.Struct: + if implementsInterface(t) && t.Name() != "" { + return t.Name() + } + return "" + case reflect.String: + name := t.Name() + if name != "" && name != "string" { + return name + } + return "String" + case reflect.Bool: + name := t.Name() + if name != "" && name != "bool" { + return name + } + return "True or False" + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + name := t.Name() + if name != "" && !strings.HasPrefix(name, "int") { + return name + } + return "Integer" + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + name := t.Name() + if name != "" && !strings.HasPrefix(name, "uint") { + return name + } + return "Unsigned Integer" + case reflect.Float32, reflect.Float64: + name := t.Name() + if name != "" && !strings.HasPrefix(name, "float") { + return name + } + return "Float" + } + return fmt.Sprintf("%+v", t) +} + +// Usage writes usage information to stderr using the default header and table format +func Usage(prefix string, spec interface{}) error { + // The default is to output the usage information as a table + // Create tabwriter instance to support table output + tabs := tabwriter.NewWriter(os.Stdout, 1, 0, 4, ' ', 0) + + err := Usagef(prefix, spec, tabs, DefaultTableFormat) + tabs.Flush() + return err +} + +// Usagef writes usage information to the specified io.Writer using the specifed template specification +func Usagef(prefix string, spec interface{}, out io.Writer, format string) error { + + // Specify the default usage template functions + functions := template.FuncMap{ + "usage_key": func(v varInfo) string { return v.Key }, + "usage_description": func(v varInfo) string { return v.Tags.Get("desc") }, + "usage_type": func(v varInfo) string { return toTypeDescription(v.Field.Type()) }, + "usage_default": func(v varInfo) string { return v.Tags.Get("default") }, + "usage_required": func(v varInfo) (string, error) { + req := v.Tags.Get("required") + if req != "" { + reqB, err := strconv.ParseBool(req) + if err != nil { + return "", err + } + if reqB { + req = "true" + } + } + return req, nil + }, + } + + tmpl, err := template.New("envconfig").Funcs(functions).Parse(format) + if err != nil { + return err + } + + return Usaget(prefix, spec, out, tmpl) +} + +// Usaget writes usage information to the specified io.Writer using the specified template +func Usaget(prefix string, spec interface{}, out io.Writer, tmpl *template.Template) error { + // gather first + infos, err := gatherInfo(prefix, spec) + if err != nil { + return err + } + + return tmpl.Execute(out, infos) +} diff --git a/vendor/vendor.json b/vendor/vendor.json index 2f0ab127f..4939492e3 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -149,10 +149,12 @@ "revisionTime": "2016-09-10T22:24:44Z" }, { - "checksumSHA1": "ngWgR7cRhH340bOHEpLTJCY1jTs=", + "checksumSHA1": "4szkOYHAJsnVGtjdfE41n8x+iuE=", "path": "github.com/kelseyhightower/envconfig", - "revision": "5c008110b20b657eb7e005b83d0a5f6aa6bb5f4b", - "revisionTime": "2016-12-05T22:33:43Z" + "revision": "f611eb38b3875cc3bd991ca91c51d06446afa14c", + "revisionTime": "2017-01-24T16:28:13Z", + "version": "v1", + "versionExact": "v1.3.0" }, { "checksumSHA1": "r6ZMyP/HsdeToXIxvgP3ntL6Bds=", |