wip modernize code base
This commit is contained in:
parent
c562a21e8c
commit
2579999de0
37 changed files with 11104 additions and 190 deletions
13
vendor/github.com/kelseyhightower/envconfig/.travis.yml
generated
vendored
Normal file
13
vendor/github.com/kelseyhightower/envconfig/.travis.yml
generated
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
language: go
|
||||
|
||||
go:
|
||||
- 1.4.x
|
||||
- 1.5.x
|
||||
- 1.6.x
|
||||
- 1.7.x
|
||||
- 1.8.x
|
||||
- 1.9.x
|
||||
- 1.10.x
|
||||
- 1.11.x
|
||||
- 1.12.x
|
||||
- tip
|
19
vendor/github.com/kelseyhightower/envconfig/LICENSE
generated
vendored
Normal file
19
vendor/github.com/kelseyhightower/envconfig/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,19 @@
|
|||
Copyright (c) 2013 Kelsey Hightower
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
2
vendor/github.com/kelseyhightower/envconfig/MAINTAINERS
generated
vendored
Normal file
2
vendor/github.com/kelseyhightower/envconfig/MAINTAINERS
generated
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
Kelsey Hightower kelsey.hightower@gmail.com github.com/kelseyhightower
|
||||
Travis Parker travis.parker@gmail.com github.com/teepark
|
192
vendor/github.com/kelseyhightower/envconfig/README.md
generated
vendored
Normal file
192
vendor/github.com/kelseyhightower/envconfig/README.md
generated
vendored
Normal file
|
@ -0,0 +1,192 @@
|
|||
# envconfig
|
||||
|
||||
[](https://travis-ci.org/kelseyhightower/envconfig)
|
||||
|
||||
```Go
|
||||
import "github.com/kelseyhightower/envconfig"
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
See [godoc](http://godoc.org/github.com/kelseyhightower/envconfig)
|
||||
|
||||
## Usage
|
||||
|
||||
Set some environment variables:
|
||||
|
||||
```Bash
|
||||
export MYAPP_DEBUG=false
|
||||
export MYAPP_PORT=8080
|
||||
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:
|
||||
|
||||
```Go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/kelseyhightower/envconfig"
|
||||
)
|
||||
|
||||
type Specification struct {
|
||||
Debug bool
|
||||
Port int
|
||||
User string
|
||||
Users []string
|
||||
Rate float32
|
||||
Timeout time.Duration
|
||||
ColorCodes map[string]int
|
||||
}
|
||||
|
||||
func main() {
|
||||
var s Specification
|
||||
err := envconfig.Process("myapp", &s)
|
||||
if err != nil {
|
||||
log.Fatal(err.Error())
|
||||
}
|
||||
format := "Debug: %v\nPort: %d\nUser: %s\nRate: %f\nTimeout: %s\n"
|
||||
_, err = fmt.Printf(format, s.Debug, s.Port, s.User, s.Rate, s.Timeout)
|
||||
if err != nil {
|
||||
log.Fatal(err.Error())
|
||||
}
|
||||
|
||||
fmt.Println("Users:")
|
||||
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)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Results:
|
||||
|
||||
```Bash
|
||||
Debug: false
|
||||
Port: 8080
|
||||
User: Kelsey
|
||||
Rate: 0.500000
|
||||
Timeout: 3m0s
|
||||
Users:
|
||||
rob
|
||||
ken
|
||||
robert
|
||||
Color codes:
|
||||
red: 1
|
||||
green: 2
|
||||
blue: 3
|
||||
```
|
||||
|
||||
## Struct Tag Support
|
||||
|
||||
Envconfig supports the use of struct tags to specify alternate, default, and required
|
||||
environment variables.
|
||||
|
||||
For example, consider the following struct:
|
||||
|
||||
```Go
|
||||
type Specification struct {
|
||||
ManualOverride1 string `envconfig:"manual_override_1"`
|
||||
DefaultVar string `default:"foobar"`
|
||||
RequiredVar string `required:"true"`
|
||||
IgnoredVar string `ignored:"true"`
|
||||
AutoSplitVar string `split_words:"true"`
|
||||
RequiredAndAutoSplitVar string `required:"true" split_words:"true"`
|
||||
}
|
||||
```
|
||||
|
||||
Envconfig has automatic support for CamelCased struct elements when the
|
||||
`split_words:"true"` tag is supplied. Without this tag, `AutoSplitVar` above
|
||||
would look for an environment variable called `MYAPP_AUTOSPLITVAR`. With the
|
||||
setting applied it will look for `MYAPP_AUTO_SPLIT_VAR`. Note that numbers
|
||||
will get globbed into the previous word. If the setting does not do the
|
||||
right thing, you may use a manual override.
|
||||
|
||||
Envconfig will process value for `ManualOverride1` by populating it with the
|
||||
value for `MYAPP_MANUAL_OVERRIDE_1`. Without this struct tag, it would have
|
||||
instead looked up `MYAPP_MANUALOVERRIDE1`. With the `split_words:"true"` tag
|
||||
it would have looked up `MYAPP_MANUAL_OVERRIDE1`.
|
||||
|
||||
```Bash
|
||||
export MYAPP_MANUAL_OVERRIDE_1="this will be the value"
|
||||
|
||||
# export MYAPP_MANUALOVERRIDE1="and this will not"
|
||||
```
|
||||
|
||||
If envconfig can't find an environment variable value for `MYAPP_DEFAULTVAR`,
|
||||
it will populate it with "foobar" as a default value.
|
||||
|
||||
If envconfig can't find an environment variable value for `MYAPP_REQUIREDVAR`,
|
||||
it will return an error when asked to process the struct. If
|
||||
`MYAPP_REQUIREDVAR` is present but empty, envconfig will not return an error.
|
||||
|
||||
If envconfig can't find an environment variable in the form `PREFIX_MYVAR`, and there
|
||||
is a struct tag defined, it will try to populate your variable with an environment
|
||||
variable that directly matches the envconfig tag in your struct definition:
|
||||
|
||||
```shell
|
||||
export SERVICE_HOST=127.0.0.1
|
||||
export MYAPP_DEBUG=true
|
||||
```
|
||||
```Go
|
||||
type Specification struct {
|
||||
ServiceHost string `envconfig:"SERVICE_HOST"`
|
||||
Debug bool
|
||||
}
|
||||
```
|
||||
|
||||
Envconfig won't process a field with the "ignored" tag set to "true", even if a corresponding
|
||||
environment variable is set.
|
||||
|
||||
## Supported Struct Field Types
|
||||
|
||||
envconfig supports these struct field types:
|
||||
|
||||
* string
|
||||
* 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)
|
||||
* [encoding.BinaryUnmarshaler](https://golang.org/pkg/encoding/#BinaryUnmarshaler)
|
||||
* [time.Duration](https://golang.org/pkg/time/#Duration)
|
||||
|
||||
Embedded structs using these fields are also supported.
|
||||
|
||||
## Custom Decoders
|
||||
|
||||
Any field whose type (or pointer-to-type) implements `envconfig.Decoder` can
|
||||
control its own deserialization:
|
||||
|
||||
```Bash
|
||||
export DNS_SERVER=8.8.8.8
|
||||
```
|
||||
|
||||
```Go
|
||||
type IPDecoder net.IP
|
||||
|
||||
func (ipd *IPDecoder) Decode(value string) error {
|
||||
*ipd = IPDecoder(net.ParseIP(value))
|
||||
return nil
|
||||
}
|
||||
|
||||
type DNSConfig struct {
|
||||
Address IPDecoder `envconfig:"DNS_SERVER"`
|
||||
}
|
||||
```
|
||||
|
||||
Also, envconfig will use a `Set(string) error` method like from the
|
||||
[flag.Value](https://godoc.org/flag#Value) interface if implemented.
|
8
vendor/github.com/kelseyhightower/envconfig/doc.go
generated
vendored
Normal file
8
vendor/github.com/kelseyhightower/envconfig/doc.go
generated
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
// Copyright (c) 2013 Kelsey Hightower. All rights reserved.
|
||||
// Use of this source code is governed by the MIT License that can be found in
|
||||
// the LICENSE file.
|
||||
|
||||
// Package envconfig implements decoding of environment variables based on a user
|
||||
// defined specification. A typical use is using environment variables for
|
||||
// configuration settings.
|
||||
package envconfig
|
7
vendor/github.com/kelseyhightower/envconfig/env_os.go
generated
vendored
Normal file
7
vendor/github.com/kelseyhightower/envconfig/env_os.go
generated
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
// +build appengine go1.5
|
||||
|
||||
package envconfig
|
||||
|
||||
import "os"
|
||||
|
||||
var lookupEnv = os.LookupEnv
|
7
vendor/github.com/kelseyhightower/envconfig/env_syscall.go
generated
vendored
Normal file
7
vendor/github.com/kelseyhightower/envconfig/env_syscall.go
generated
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
// +build !appengine,!go1.5
|
||||
|
||||
package envconfig
|
||||
|
||||
import "syscall"
|
||||
|
||||
var lookupEnv = syscall.Getenv
|
382
vendor/github.com/kelseyhightower/envconfig/envconfig.go
generated
vendored
Normal file
382
vendor/github.com/kelseyhightower/envconfig/envconfig.go
generated
vendored
Normal file
|
@ -0,0 +1,382 @@
|
|||
// Copyright (c) 2013 Kelsey Hightower. 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"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// ErrInvalidSpecification indicates that a specification is of the wrong type.
|
||||
var ErrInvalidSpecification = errors.New("specification must be a struct pointer")
|
||||
|
||||
var gatherRegexp = regexp.MustCompile("([^A-Z]+|[A-Z]+[^A-Z]+|[A-Z]+)")
|
||||
var acronymRegexp = regexp.MustCompile("([A-Z]+)([A-Z][^A-Z]+)")
|
||||
|
||||
// A ParseError occurs when an environment variable cannot be converted to
|
||||
// the type required by a struct field during assignment.
|
||||
type ParseError struct {
|
||||
KeyName string
|
||||
FieldName string
|
||||
TypeName string
|
||||
Value string
|
||||
Err error
|
||||
}
|
||||
|
||||
// Decoder has the same semantics as Setter, but takes higher precedence.
|
||||
// It is provided for historical compatibility.
|
||||
type Decoder interface {
|
||||
Decode(value string) error
|
||||
}
|
||||
|
||||
// Setter is implemented by types can self-deserialize values.
|
||||
// Any type that implements flag.Value also implements Setter.
|
||||
type Setter interface {
|
||||
Set(value string) error
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
// 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) {
|
||||
s := reflect.ValueOf(spec)
|
||||
|
||||
if s.Kind() != reflect.Ptr {
|
||||
return nil, ErrInvalidSpecification
|
||||
}
|
||||
s = s.Elem()
|
||||
if s.Kind() != reflect.Struct {
|
||||
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)
|
||||
if !f.CanSet() || isTrue(ftype.Tag.Get("ignored")) {
|
||||
continue
|
||||
}
|
||||
|
||||
for f.Kind() == reflect.Ptr {
|
||||
if f.IsNil() {
|
||||
if f.Type().Elem().Kind() != reflect.Struct {
|
||||
// nil pointer to a non-struct: leave it alone
|
||||
break
|
||||
}
|
||||
// nil pointer to struct: create a zero instance
|
||||
f.Set(reflect.New(f.Type().Elem()))
|
||||
}
|
||||
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)
|
||||
info.Key = info.Name
|
||||
|
||||
// Best effort to un-pick camel casing as separate words
|
||||
if isTrue(ftype.Tag.Get("split_words")) {
|
||||
words := gatherRegexp.FindAllStringSubmatch(ftype.Name, -1)
|
||||
if len(words) > 0 {
|
||||
var name []string
|
||||
for _, words := range words {
|
||||
if m := acronymRegexp.FindStringSubmatch(words[0]); len(m) == 3 {
|
||||
name = append(name, m[1], m[2])
|
||||
} else {
|
||||
name = append(name, words[0])
|
||||
}
|
||||
}
|
||||
|
||||
info.Key = strings.Join(name, "_")
|
||||
}
|
||||
}
|
||||
if info.Alt != "" {
|
||||
info.Key = info.Alt
|
||||
}
|
||||
if prefix != "" {
|
||||
info.Key = fmt.Sprintf("%s_%s", prefix, info.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 && binaryUnmarshaler(f) == nil {
|
||||
innerPrefix := prefix
|
||||
if !ftype.Anonymous {
|
||||
innerPrefix = info.Key
|
||||
}
|
||||
|
||||
embeddedPtr := f.Addr().Interface()
|
||||
embeddedInfos, err := gatherInfo(innerPrefix, embeddedPtr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
infos = append(infos[:len(infos)-1], embeddedInfos...)
|
||||
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
return infos, nil
|
||||
}
|
||||
|
||||
// CheckDisallowed checks that no environment variables with the prefix are set
|
||||
// that we don't know how or want to parse. This is likely only meaningful with
|
||||
// a non-empty prefix.
|
||||
func CheckDisallowed(prefix string, spec interface{}) error {
|
||||
infos, err := gatherInfo(prefix, spec)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
vars := make(map[string]struct{})
|
||||
for _, info := range infos {
|
||||
vars[info.Key] = struct{}{}
|
||||
}
|
||||
|
||||
if prefix != "" {
|
||||
prefix = strings.ToUpper(prefix) + "_"
|
||||
}
|
||||
|
||||
for _, env := range os.Environ() {
|
||||
if !strings.HasPrefix(env, prefix) {
|
||||
continue
|
||||
}
|
||||
v := strings.SplitN(env, "=", 2)[0]
|
||||
if _, found := vars[v]; !found {
|
||||
return fmt.Errorf("unknown environment variable %s", v)
|
||||
}
|
||||
}
|
||||
|
||||
return 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(info.Key)
|
||||
if !ok && info.Alt != "" {
|
||||
value, ok = lookupEnv(info.Alt)
|
||||
}
|
||||
|
||||
def := info.Tags.Get("default")
|
||||
if def != "" && !ok {
|
||||
value = def
|
||||
}
|
||||
|
||||
req := info.Tags.Get("required")
|
||||
if !ok && def == "" {
|
||||
if isTrue(req) {
|
||||
key := info.Key
|
||||
if info.Alt != "" {
|
||||
key = info.Alt
|
||||
}
|
||||
return fmt.Errorf("required key %s missing value", key)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
err = processField(value, info.Field)
|
||||
if err != nil {
|
||||
return &ParseError{
|
||||
KeyName: info.Key,
|
||||
FieldName: info.Name,
|
||||
TypeName: info.Field.Type().String(),
|
||||
Value: value,
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// MustProcess is the same as Process but panics if an error occurs
|
||||
func MustProcess(prefix string, spec interface{}) {
|
||||
if err := Process(prefix, spec); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func processField(value string, field reflect.Value) error {
|
||||
typ := field.Type()
|
||||
|
||||
decoder := decoderFrom(field)
|
||||
if decoder != nil {
|
||||
return decoder.Decode(value)
|
||||
}
|
||||
// look for Set method if Decode not defined
|
||||
setter := setterFrom(field)
|
||||
if setter != nil {
|
||||
return setter.Set(value)
|
||||
}
|
||||
|
||||
if t := textUnmarshaler(field); t != nil {
|
||||
return t.UnmarshalText([]byte(value))
|
||||
}
|
||||
|
||||
if b := binaryUnmarshaler(field); b != nil {
|
||||
return b.UnmarshalBinary([]byte(value))
|
||||
}
|
||||
|
||||
if typ.Kind() == reflect.Ptr {
|
||||
typ = typ.Elem()
|
||||
if field.IsNil() {
|
||||
field.Set(reflect.New(typ))
|
||||
}
|
||||
field = field.Elem()
|
||||
}
|
||||
|
||||
switch typ.Kind() {
|
||||
case reflect.String:
|
||||
field.SetString(value)
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
var (
|
||||
val int64
|
||||
err error
|
||||
)
|
||||
if field.Kind() == reflect.Int64 && typ.PkgPath() == "time" && typ.Name() == "Duration" {
|
||||
var d time.Duration
|
||||
d, err = time.ParseDuration(value)
|
||||
val = int64(d)
|
||||
} else {
|
||||
val, err = strconv.ParseInt(value, 0, typ.Bits())
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
field.SetInt(val)
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
val, err := strconv.ParseUint(value, 0, typ.Bits())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
field.SetUint(val)
|
||||
case reflect.Bool:
|
||||
val, err := strconv.ParseBool(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
field.SetBool(val)
|
||||
case reflect.Float32, reflect.Float64:
|
||||
val, err := strconv.ParseFloat(value, typ.Bits())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
field.SetFloat(val)
|
||||
case reflect.Slice:
|
||||
sl := reflect.MakeSlice(typ, 0, 0)
|
||||
if typ.Elem().Kind() == reflect.Uint8 {
|
||||
sl = reflect.ValueOf([]byte(value))
|
||||
} else if len(strings.TrimSpace(value)) != 0 {
|
||||
vals := strings.Split(value, ",")
|
||||
sl = reflect.MakeSlice(typ, len(vals), len(vals))
|
||||
for i, val := range vals {
|
||||
err := processField(val, sl.Index(i))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
field.Set(sl)
|
||||
case reflect.Map:
|
||||
mp := reflect.MakeMap(typ)
|
||||
if len(strings.TrimSpace(value)) != 0 {
|
||||
pairs := strings.Split(value, ",")
|
||||
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
|
||||
}
|
||||
|
||||
func interfaceFrom(field reflect.Value, fn func(interface{}, *bool)) {
|
||||
// it may be impossible for a struct field to fail this check
|
||||
if !field.CanInterface() {
|
||||
return
|
||||
}
|
||||
var ok bool
|
||||
fn(field.Interface(), &ok)
|
||||
if !ok && field.CanAddr() {
|
||||
fn(field.Addr().Interface(), &ok)
|
||||
}
|
||||
}
|
||||
|
||||
func decoderFrom(field reflect.Value) (d Decoder) {
|
||||
interfaceFrom(field, func(v interface{}, ok *bool) { d, *ok = v.(Decoder) })
|
||||
return d
|
||||
}
|
||||
|
||||
func setterFrom(field reflect.Value) (s Setter) {
|
||||
interfaceFrom(field, func(v interface{}, ok *bool) { s, *ok = v.(Setter) })
|
||||
return s
|
||||
}
|
||||
|
||||
func textUnmarshaler(field reflect.Value) (t encoding.TextUnmarshaler) {
|
||||
interfaceFrom(field, func(v interface{}, ok *bool) { t, *ok = v.(encoding.TextUnmarshaler) })
|
||||
return t
|
||||
}
|
||||
|
||||
func binaryUnmarshaler(field reflect.Value) (b encoding.BinaryUnmarshaler) {
|
||||
interfaceFrom(field, func(v interface{}, ok *bool) { b, *ok = v.(encoding.BinaryUnmarshaler) })
|
||||
return b
|
||||
}
|
||||
|
||||
func isTrue(s string) bool {
|
||||
b, _ := strconv.ParseBool(s)
|
||||
return b
|
||||
}
|
1
vendor/github.com/kelseyhightower/envconfig/go.mod
generated
vendored
Normal file
1
vendor/github.com/kelseyhightower/envconfig/go.mod
generated
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
module github.com/kelseyhightower/envconfig
|
164
vendor/github.com/kelseyhightower/envconfig/usage.go
generated
vendored
Normal file
164
vendor/github.com/kelseyhightower/envconfig/usage.go
generated
vendored
Normal file
|
@ -0,0 +1,164 @@
|
|||
// 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 tabular 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()
|
||||
textUnmarshalerType = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem()
|
||||
binaryUnmarshalerType = reflect.TypeOf((*encoding.BinaryUnmarshaler)(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(textUnmarshalerType) ||
|
||||
reflect.PtrTo(t).Implements(textUnmarshalerType) ||
|
||||
t.Implements(binaryUnmarshalerType) ||
|
||||
reflect.PtrTo(t).Implements(binaryUnmarshalerType)
|
||||
}
|
||||
|
||||
// toTypeDescription converts Go types into a human readable description
|
||||
func toTypeDescription(t reflect.Type) string {
|
||||
switch t.Kind() {
|
||||
case reflect.Array, reflect.Slice:
|
||||
if t.Elem().Kind() == reflect.Uint8 {
|
||||
return "String"
|
||||
}
|
||||
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 stdout 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)
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue