mirror of
https://github.com/fnproject/fn.git
synced 2022-10-28 21:29:17 +03:00
add functions/vendor files
This commit is contained in:
10
vendor/github.com/vrischmann/envconfig/.travis.yml
generated
vendored
Normal file
10
vendor/github.com/vrischmann/envconfig/.travis.yml
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.2
|
||||
- 1.3
|
||||
- 1.4
|
||||
- 1.5
|
||||
- 1.6
|
||||
- 1.7
|
||||
- tip
|
||||
19
vendor/github.com/vrischmann/envconfig/LICENSE
generated
vendored
Normal file
19
vendor/github.com/vrischmann/envconfig/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
Copyright (c) 2015 Vincent Rischmann
|
||||
|
||||
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.
|
||||
151
vendor/github.com/vrischmann/envconfig/README.md
generated
vendored
Normal file
151
vendor/github.com/vrischmann/envconfig/README.md
generated
vendored
Normal file
@@ -0,0 +1,151 @@
|
||||
envconfig
|
||||
=========
|
||||
|
||||
[](https://travis-ci.org/vrischmann/envconfig)
|
||||
[](https://godoc.org/github.com/vrischmann/envconfig)
|
||||
|
||||
envconfig is a library which allows you to parse your configuration from environment variables and fill an arbitrary struct.
|
||||
|
||||
See [the example](https://godoc.org/github.com/vrischmann/envconfig#example-Init) to understand how to use it, it's pretty simple.
|
||||
|
||||
Supported types
|
||||
---------------
|
||||
|
||||
* Almost all standard types plus `time.Duration` are supported by default.
|
||||
* Slices and arrays
|
||||
* Arbitrary structs
|
||||
* Custom types via the [Unmarshaler](https://godoc.org/github.com/vrischmann/envconfig/#Unmarshaler) interface.
|
||||
|
||||
How does it work
|
||||
----------------
|
||||
|
||||
*envconfig* takes the hierarchy of your configuration struct and the names of the fields to create a environment variable key.
|
||||
|
||||
For example:
|
||||
|
||||
```go
|
||||
var conf struct {
|
||||
Name string
|
||||
Shard struct {
|
||||
Host string
|
||||
Port int
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This will check for those 3 keys:
|
||||
|
||||
* NAME or name
|
||||
* SHARD\_HOST, or shard\_host
|
||||
* SHARD\_PORT, or shard\_port
|
||||
|
||||
Flexible key naming
|
||||
-------------------
|
||||
|
||||
*envconfig* supports having underscores in the key names where there is a _word boundary_. Now, that term is not super explicit, so let me show you an example:
|
||||
|
||||
```go
|
||||
var conf struct {
|
||||
Cassandra struct {
|
||||
SSLCert string
|
||||
SslKey string
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This will check all of the following keys:
|
||||
|
||||
* CASSANDRA\_SSL\_CERT, CASSANDRA\_SSLCERT, cassandra\_ssl\_cert, cassandra\_sslcert
|
||||
* CASSANDRA\_SSL\_KEY, CASSANDRA\_SSLKEY, cassandra\_ssl\_key, cassandra\_sslkey
|
||||
|
||||
If that is not good enough, look just below.
|
||||
|
||||
Custom environment variable names
|
||||
---------------------------------
|
||||
|
||||
*envconfig* supports custom environment variable names:
|
||||
|
||||
```go
|
||||
var conf struct {
|
||||
Name string `envconfig:"myName"`
|
||||
}
|
||||
```
|
||||
|
||||
Default values
|
||||
--------------
|
||||
|
||||
*envconfig* supports default values:
|
||||
|
||||
```go
|
||||
var conf struct {
|
||||
Name string `envconfig:"default=Vincent"`
|
||||
}
|
||||
```
|
||||
|
||||
Optional values
|
||||
---------------
|
||||
|
||||
*envconfig* supports optional values:
|
||||
|
||||
```go
|
||||
var conf struct {
|
||||
Name string `envconfig:"optional"`
|
||||
Age int `envconfig:"-"`
|
||||
}
|
||||
```
|
||||
|
||||
The two syntax are equivalent.
|
||||
|
||||
Combining multiple options in one tag
|
||||
-------------------------------------
|
||||
|
||||
You can of course combine multiple options:
|
||||
|
||||
```go
|
||||
var conf struct {
|
||||
Name string `envconfig:"default=Vincent,myName"`
|
||||
}
|
||||
```
|
||||
|
||||
Slices or arrays
|
||||
----------------
|
||||
|
||||
With slices or arrays, the same naming is applied for the slice. To put multiple elements into the slice or array, you need to separate
|
||||
them with a *,* (will probably be configurable in the future, or at least have a way to escape)
|
||||
|
||||
For example:
|
||||
|
||||
```go
|
||||
var conf struct {
|
||||
Ports []int
|
||||
}
|
||||
```
|
||||
|
||||
This will check for the key __PORTS__:
|
||||
|
||||
* if your variable is *9000* the slice will contain only 9000
|
||||
* if your variable is *9000,100* the slice will contain 9000 and 100
|
||||
|
||||
For slices of structs, it's a little more complicated. The same splitting of slice elements is done with a *comma*, however, each token must follow
|
||||
a specific format like this: `{<first field>,<second field>,...}`
|
||||
|
||||
For example:
|
||||
|
||||
```go
|
||||
var conf struct {
|
||||
Shards []struct {
|
||||
Name string
|
||||
Port int
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This will check for the key __SHARDS__. Example variable content: `{foobar,9000},{barbaz,20000}`
|
||||
|
||||
This will result in two struct defined in the *Shards* slice.
|
||||
|
||||
Future work
|
||||
-----------
|
||||
|
||||
* support for time.Time values with a layout defined via a field tag
|
||||
* support for complex types
|
||||
199
vendor/github.com/vrischmann/envconfig/doc.go
generated
vendored
Normal file
199
vendor/github.com/vrischmann/envconfig/doc.go
generated
vendored
Normal file
@@ -0,0 +1,199 @@
|
||||
/*
|
||||
Package envconfig implements a configuration reader which reads each value from an environment variable.
|
||||
|
||||
The basic idea is that you define a configuration struct, like this:
|
||||
|
||||
var conf struct {
|
||||
Addr string
|
||||
Port int
|
||||
Auth struct {
|
||||
Key string
|
||||
Endpoint string
|
||||
}
|
||||
Partitions []int
|
||||
Shards []struct {
|
||||
Name string
|
||||
Id int
|
||||
}
|
||||
}
|
||||
|
||||
Once you have that, you need to initialize the configuration:
|
||||
|
||||
if err := envconfig.Init(&conf); err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
Then it's just a matter of setting the environment variables when calling your binary:
|
||||
|
||||
ADDR=localhost PORT=6379 AUTH_KEY=foobar ./mybinary
|
||||
|
||||
Layout of the conf struct
|
||||
|
||||
Your conf struct must follow the following rules:
|
||||
- no unexported fields by default (can turn off with Options.AllowUnexported)
|
||||
- only supported types (no map fields for example)
|
||||
|
||||
Naming of the keys
|
||||
|
||||
By default, envconfig generates all possible keys based on the field chain according to a flexible naming scheme.
|
||||
|
||||
The field chain is how you access your field in the configuration struct. For example:
|
||||
|
||||
var conf struct {
|
||||
Shard struct {
|
||||
Name string
|
||||
}
|
||||
}
|
||||
|
||||
With that struct, you access the name field via the chain *Shard.Name*
|
||||
|
||||
The default naming scheme takes that and transforms it into the following:
|
||||
- SHARD_NAME
|
||||
- shard_name
|
||||
|
||||
It can handles more complicated cases, with multiple words in one field name. It needs to be in the correct case though, for example:
|
||||
|
||||
var conf struct {
|
||||
Cassandra struct {
|
||||
SSLCert string
|
||||
SslKey string
|
||||
}
|
||||
}
|
||||
|
||||
With that struct, you access the name field via the chain *Cassandra.SSLCert* or *Cassandra.SslKey*
|
||||
|
||||
The default naming scheme takes that and transforms it into the following:
|
||||
- CASSANDRA_SSL_CERT, cassandra_ssl_cert, CASSANDRA_SSLCERT, cassandra_sslcert
|
||||
- CASSANDRA_SSL_KEY, cassandra_ssl_key, CASSANDRA_SSLKEY, cassandra_sslkey
|
||||
|
||||
And, if that is not good enough for you, you always have the option to use a custom key:
|
||||
|
||||
var conf struct {
|
||||
Cassandra struct {
|
||||
Name string `envconfig:"cassandraMyName"`
|
||||
}
|
||||
}
|
||||
|
||||
Now envconfig will only ever checks the environment variable _cassandraMyName_.
|
||||
|
||||
|
||||
Content of the variables
|
||||
|
||||
There are three types of content for a single variable:
|
||||
- for simple types, a single string representing the value, and parseable into the type.
|
||||
- for slices or arrays, a comma-separated list of strings. Each string must be parseable into the element type of the slice or array.
|
||||
- for structs, a comma-separated list of specially formatted strings representing structs.
|
||||
|
||||
Example of a valid slice value:
|
||||
foo,bar,baz
|
||||
|
||||
The format for a struct is as follow:
|
||||
- prefixed with {
|
||||
- suffixed with }
|
||||
- contains a comma-separated list of field values, in the order in which they are defined in the struct
|
||||
|
||||
Example of a valid struct value:
|
||||
type MyStruct struct {
|
||||
Name string
|
||||
Id int
|
||||
Timeout time.Duration
|
||||
}
|
||||
|
||||
{foobar,10,120s}
|
||||
|
||||
Example of a valid slice of struct values:
|
||||
{foobar,10,120s},{barbaz,20,50s}
|
||||
|
||||
Special case for bytes slices
|
||||
|
||||
For bytes slices, you generally don't want to type out a comma-separated list of byte values.
|
||||
|
||||
For this use case, we support base64 encoded values.
|
||||
|
||||
Here's an example:
|
||||
|
||||
var conf struct {
|
||||
Data []byte
|
||||
}
|
||||
|
||||
os.Setenv("DATA", "Rk9PQkFS")
|
||||
|
||||
This will decode DATA to FOOBAR and put that into conf.Data.
|
||||
|
||||
Optional values
|
||||
|
||||
Sometimes you don't absolutely need a value. Here's how we tell envconfig a value is optional:
|
||||
|
||||
var conf struct {
|
||||
Name string `envconfig:"optional"`
|
||||
Age int `envconfig:"-"`
|
||||
}
|
||||
|
||||
The two syntax are equivalent.
|
||||
|
||||
Default values
|
||||
|
||||
Often times you have configuration keys which almost never changes, but you still want to be able to change them.
|
||||
|
||||
In such cases, you might want to provide a default value.
|
||||
|
||||
Here's to do this with envconfig:
|
||||
|
||||
var conf struct {
|
||||
Timeout time.Duration `envconfig:"default=1m"`
|
||||
}
|
||||
|
||||
Combining options
|
||||
|
||||
You can of course combine multiple options. The syntax is simple enough, separate each option with a comma.
|
||||
|
||||
For example:
|
||||
|
||||
var conf struct {
|
||||
Timeout time.Duration `envconfig:"default=1m,myTimeout"`
|
||||
}
|
||||
|
||||
This would give you the default timeout of 1 minute, and lookup the myTimeout environment variable.
|
||||
|
||||
Supported types
|
||||
|
||||
envconfig supports the following list of types:
|
||||
|
||||
- bool
|
||||
- string
|
||||
- intX
|
||||
- uintX
|
||||
- floatX
|
||||
- time.Duration
|
||||
- pointers to all of the above types
|
||||
|
||||
Notably, we don't (yet) support complex types simply because I had no use for it yet.
|
||||
|
||||
Custom unmarshaler
|
||||
|
||||
When the standard types are not enough, you will want to use a custom unmarshaler for your types.
|
||||
|
||||
You do this by implementing Unmarshaler on your type. Here's an example:
|
||||
|
||||
type connectionType uint
|
||||
|
||||
const (
|
||||
tlsConnection connectionType = iota
|
||||
insecureConnection
|
||||
)
|
||||
|
||||
func (t *connectionType) Unmarshal(s string) error {
|
||||
switch s {
|
||||
case "tls":
|
||||
*t = tlsConnection
|
||||
case "insecure":
|
||||
*t = insecureConnection
|
||||
default:
|
||||
return fmt.Errorf("unable to unmarshal %s to a connection type", s)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
*/
|
||||
package envconfig
|
||||
487
vendor/github.com/vrischmann/envconfig/envconfig.go
generated
vendored
Normal file
487
vendor/github.com/vrischmann/envconfig/envconfig.go
generated
vendored
Normal file
@@ -0,0 +1,487 @@
|
||||
package envconfig
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrUnexportedField is the error returned by the Init* functions when a field of the config struct is not exported and the option AllowUnexported is not used.
|
||||
ErrUnexportedField = errors.New("envconfig: unexported field")
|
||||
// ErrNotAPointer is the error returned by the Init* functions when the configuration object is not a pointer.
|
||||
ErrNotAPointer = errors.New("envconfig: value is not a pointer")
|
||||
// ErrInvalidValueKind is the error returned by the Init* functions when the configuration object is not a struct.
|
||||
ErrInvalidValueKind = errors.New("envconfig: invalid value kind, only works on structs")
|
||||
// ErrDefaultUnsupportedOnSlice is the error returned by the Init* functions when there is a default tag on a slice.
|
||||
// The `default` tag is unsupported on slices because slice parsing uses , as the separator, as does the envconfig tags separator.
|
||||
ErrDefaultUnsupportedOnSlice = errors.New("envconfig: default tag unsupported on slice")
|
||||
)
|
||||
|
||||
type context struct {
|
||||
name string
|
||||
customName string
|
||||
defaultVal string
|
||||
parents []reflect.Value
|
||||
optional, leaveNil bool
|
||||
allowUnexported bool
|
||||
}
|
||||
|
||||
// Unmarshaler is the interface implemented by objects that can unmarshal
|
||||
// a environment variable string of themselves.
|
||||
type Unmarshaler interface {
|
||||
Unmarshal(s string) error
|
||||
}
|
||||
|
||||
// Options is used to customize the behavior of envconfig. Use it with InitWithOptions.
|
||||
type Options struct {
|
||||
// Prefix allows specifying a prefix for each key.
|
||||
Prefix string
|
||||
|
||||
// AllOptional determines whether to not throw errors by default for any key
|
||||
// that is not found. AllOptional=true means errors will not be thrown.
|
||||
AllOptional bool
|
||||
|
||||
// LeaveNil specifies whether to not create new pointers for any pointer fields
|
||||
// found within the passed config. Rather, it behaves such that if and only if
|
||||
// there is a) a non-empty field in the value or b) a non-empty value that
|
||||
// the pointer is pointing to will a new pointer be created. By default,
|
||||
// LeaveNil=false will create all pointers in all structs if they are nil.
|
||||
//
|
||||
// var X struct {
|
||||
// A *struct{
|
||||
// B string
|
||||
// }
|
||||
// }
|
||||
// envconfig.InitWithOptions(&X, Options{LeaveNil: true})
|
||||
//
|
||||
// $ ./program
|
||||
//
|
||||
// X.A == nil
|
||||
//
|
||||
// $ A_B="string" ./program
|
||||
//
|
||||
// X.A.B="string" // A will not be nil
|
||||
LeaveNil bool
|
||||
|
||||
// AllowUnexported allows unexported fields to be present in the passed config.
|
||||
AllowUnexported bool
|
||||
}
|
||||
|
||||
// Init reads the configuration from environment variables and populates the conf object. conf must be a pointer
|
||||
func Init(conf interface{}) error {
|
||||
return InitWithOptions(conf, Options{})
|
||||
}
|
||||
|
||||
// InitWithPrefix reads the configuration from environment variables and populates the conf object. conf must be a pointer.
|
||||
// Each key read will be prefixed with the prefix string.
|
||||
func InitWithPrefix(conf interface{}, prefix string) error {
|
||||
return InitWithOptions(conf, Options{Prefix: prefix})
|
||||
}
|
||||
|
||||
// InitWithOptions reads the configuration from environment variables and populates the conf object.
|
||||
// conf must be a pointer.
|
||||
func InitWithOptions(conf interface{}, opts Options) error {
|
||||
value := reflect.ValueOf(conf)
|
||||
if value.Kind() != reflect.Ptr {
|
||||
return ErrNotAPointer
|
||||
}
|
||||
|
||||
elem := value.Elem()
|
||||
|
||||
ctx := context{
|
||||
name: opts.Prefix,
|
||||
optional: opts.AllOptional,
|
||||
leaveNil: opts.LeaveNil,
|
||||
allowUnexported: opts.AllowUnexported,
|
||||
}
|
||||
switch elem.Kind() {
|
||||
case reflect.Ptr:
|
||||
if elem.IsNil() {
|
||||
elem.Set(reflect.New(elem.Type().Elem()))
|
||||
}
|
||||
_, err := readStruct(elem.Elem(), &ctx)
|
||||
return err
|
||||
case reflect.Struct:
|
||||
_, err := readStruct(elem, &ctx)
|
||||
return err
|
||||
default:
|
||||
return ErrInvalidValueKind
|
||||
}
|
||||
}
|
||||
|
||||
type tag struct {
|
||||
customName string
|
||||
optional bool
|
||||
skip bool
|
||||
defaultVal string
|
||||
}
|
||||
|
||||
func parseTag(s string) *tag {
|
||||
var t tag
|
||||
|
||||
tokens := strings.Split(s, ",")
|
||||
for _, v := range tokens {
|
||||
switch {
|
||||
case v == "-":
|
||||
t.skip = true
|
||||
case v == "optional":
|
||||
t.optional = true
|
||||
case strings.HasPrefix(v, "default="):
|
||||
t.defaultVal = strings.TrimPrefix(v, "default=")
|
||||
default:
|
||||
t.customName = v
|
||||
}
|
||||
}
|
||||
|
||||
return &t
|
||||
}
|
||||
|
||||
func readStruct(value reflect.Value, ctx *context) (nonNil bool, err error) {
|
||||
var parents []reflect.Value
|
||||
|
||||
for i := 0; i < value.NumField(); i++ {
|
||||
field := value.Field(i)
|
||||
name := value.Type().Field(i).Name
|
||||
|
||||
tag := parseTag(value.Type().Field(i).Tag.Get("envconfig"))
|
||||
if tag.skip || !field.CanSet() {
|
||||
if !field.CanSet() && !ctx.allowUnexported {
|
||||
return false, ErrUnexportedField
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
parents = ctx.parents
|
||||
|
||||
doRead:
|
||||
switch field.Kind() {
|
||||
case reflect.Ptr:
|
||||
// it's a pointer, create a new value and restart the switch
|
||||
if field.IsNil() {
|
||||
field.Set(reflect.New(field.Type().Elem()))
|
||||
parents = append(parents, field) // track parent pointers to deallocate if no children are filled in
|
||||
}
|
||||
field = field.Elem()
|
||||
goto doRead
|
||||
case reflect.Struct:
|
||||
var nonNilIn bool
|
||||
nonNilIn, err = readStruct(field, &context{
|
||||
name: combineName(ctx.name, name),
|
||||
optional: ctx.optional || tag.optional,
|
||||
defaultVal: tag.defaultVal,
|
||||
parents: parents,
|
||||
leaveNil: ctx.leaveNil,
|
||||
allowUnexported: ctx.allowUnexported,
|
||||
})
|
||||
nonNil = nonNil || nonNilIn
|
||||
default:
|
||||
var ok bool
|
||||
ok, err = setField(field, &context{
|
||||
name: combineName(ctx.name, name),
|
||||
customName: tag.customName,
|
||||
optional: ctx.optional || tag.optional,
|
||||
defaultVal: tag.defaultVal,
|
||||
parents: parents,
|
||||
leaveNil: ctx.leaveNil,
|
||||
allowUnexported: ctx.allowUnexported,
|
||||
})
|
||||
nonNil = nonNil || ok
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
|
||||
if !nonNil && ctx.leaveNil { // re-zero
|
||||
for _, p := range parents {
|
||||
p.Set(reflect.Zero(p.Type()))
|
||||
}
|
||||
}
|
||||
|
||||
return nonNil, err
|
||||
}
|
||||
|
||||
var byteSliceType = reflect.TypeOf([]byte(nil))
|
||||
|
||||
func setField(value reflect.Value, ctx *context) (ok bool, err error) {
|
||||
str, err := readValue(ctx)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if len(str) == 0 && ctx.optional {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
isSliceNotUnmarshaler := value.Kind() == reflect.Slice && !isUnmarshaler(value.Type())
|
||||
switch {
|
||||
case isSliceNotUnmarshaler && value.Type() == byteSliceType:
|
||||
return true, parseBytesValue(value, str)
|
||||
|
||||
case isSliceNotUnmarshaler:
|
||||
return true, setSliceField(value, str, ctx)
|
||||
|
||||
default:
|
||||
return true, parseValue(value, str, ctx)
|
||||
}
|
||||
}
|
||||
|
||||
func setSliceField(value reflect.Value, str string, ctx *context) error {
|
||||
if ctx.defaultVal != "" {
|
||||
return ErrDefaultUnsupportedOnSlice
|
||||
}
|
||||
|
||||
elType := value.Type().Elem()
|
||||
tnz := newSliceTokenizer(str)
|
||||
|
||||
slice := reflect.MakeSlice(value.Type(), value.Len(), value.Cap())
|
||||
|
||||
for tnz.scan() {
|
||||
token := tnz.text()
|
||||
|
||||
el := reflect.New(elType).Elem()
|
||||
|
||||
if err := parseValue(el, token, ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
slice = reflect.Append(slice, el)
|
||||
}
|
||||
|
||||
value.Set(slice)
|
||||
|
||||
return tnz.Err()
|
||||
}
|
||||
|
||||
var (
|
||||
durationType = reflect.TypeOf(new(time.Duration)).Elem()
|
||||
unmarshalerType = reflect.TypeOf(new(Unmarshaler)).Elem()
|
||||
)
|
||||
|
||||
func isDurationField(t reflect.Type) bool {
|
||||
return t.AssignableTo(durationType)
|
||||
}
|
||||
|
||||
func isUnmarshaler(t reflect.Type) bool {
|
||||
return t.Implements(unmarshalerType) || reflect.PtrTo(t).Implements(unmarshalerType)
|
||||
}
|
||||
|
||||
func parseValue(v reflect.Value, str string, ctx *context) (err error) {
|
||||
vtype := v.Type()
|
||||
|
||||
// Special case when the type is a map: we need to make the map
|
||||
switch vtype.Kind() {
|
||||
case reflect.Map:
|
||||
v.Set(reflect.MakeMap(vtype))
|
||||
}
|
||||
|
||||
// Special case for Unmarshaler
|
||||
if isUnmarshaler(vtype) {
|
||||
return parseWithUnmarshaler(v, str)
|
||||
}
|
||||
|
||||
// Special case for time.Duration
|
||||
if isDurationField(vtype) {
|
||||
return parseDuration(v, str)
|
||||
}
|
||||
|
||||
kind := vtype.Kind()
|
||||
switch kind {
|
||||
case reflect.Bool:
|
||||
err = parseBoolValue(v, str)
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
err = parseIntValue(v, str)
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
err = parseUintValue(v, str)
|
||||
case reflect.Float32, reflect.Float64:
|
||||
err = parseFloatValue(v, str)
|
||||
case reflect.Ptr:
|
||||
v.Set(reflect.New(vtype.Elem()))
|
||||
return parseValue(v.Elem(), str, ctx)
|
||||
case reflect.String:
|
||||
v.SetString(str)
|
||||
case reflect.Struct:
|
||||
err = parseStruct(v, str, ctx)
|
||||
default:
|
||||
return fmt.Errorf("envconfig: kind %v not supported", kind)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func parseWithUnmarshaler(v reflect.Value, str string) error {
|
||||
var u = v.Addr().Interface().(Unmarshaler)
|
||||
return u.Unmarshal(str)
|
||||
}
|
||||
|
||||
func parseDuration(v reflect.Value, str string) error {
|
||||
d, err := time.ParseDuration(str)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
v.SetInt(int64(d))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// NOTE(vincent): this is only called when parsing structs inside a slice.
|
||||
func parseStruct(value reflect.Value, token string, ctx *context) error {
|
||||
tokens := strings.Split(token[1:len(token)-1], ",")
|
||||
if len(tokens) != value.NumField() {
|
||||
return fmt.Errorf("envconfig: struct token has %d fields but struct has %d", len(tokens), value.NumField())
|
||||
}
|
||||
|
||||
for i := 0; i < value.NumField(); i++ {
|
||||
field := value.Field(i)
|
||||
t := tokens[i]
|
||||
|
||||
if err := parseValue(field, t, ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseBoolValue(v reflect.Value, str string) error {
|
||||
val, err := strconv.ParseBool(str)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v.SetBool(val)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseIntValue(v reflect.Value, str string) error {
|
||||
val, err := strconv.ParseInt(str, 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v.SetInt(val)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseUintValue(v reflect.Value, str string) error {
|
||||
val, err := strconv.ParseUint(str, 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v.SetUint(val)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseFloatValue(v reflect.Value, str string) error {
|
||||
val, err := strconv.ParseFloat(str, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v.SetFloat(val)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseBytesValue(v reflect.Value, str string) error {
|
||||
val, err := base64.StdEncoding.DecodeString(str)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v.SetBytes(val)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func combineName(parentName, name string) string {
|
||||
if parentName == "" {
|
||||
return name
|
||||
}
|
||||
|
||||
return parentName + "." + name
|
||||
}
|
||||
|
||||
func readValue(ctx *context) (string, error) {
|
||||
keys := makeAllPossibleKeys(ctx)
|
||||
|
||||
var str string
|
||||
|
||||
for _, key := range keys {
|
||||
str = os.Getenv(key)
|
||||
if str != "" {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if str != "" {
|
||||
return str, nil
|
||||
}
|
||||
|
||||
if ctx.defaultVal != "" {
|
||||
return ctx.defaultVal, nil
|
||||
}
|
||||
|
||||
if ctx.optional {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("envconfig: keys %s not found", strings.Join(keys, ", "))
|
||||
}
|
||||
|
||||
func makeAllPossibleKeys(ctx *context) (res []string) {
|
||||
if ctx.customName != "" {
|
||||
return []string{ctx.customName}
|
||||
}
|
||||
|
||||
tmp := make(map[string]struct{})
|
||||
{
|
||||
n := []rune(ctx.name)
|
||||
|
||||
var buf bytes.Buffer // this is the buffer where we put extra underscores on "word" boundaries
|
||||
var buf2 bytes.Buffer // this is the buffer with the standard naming scheme
|
||||
|
||||
wroteUnderscore := false
|
||||
for i, r := range ctx.name {
|
||||
if r == '.' {
|
||||
buf.WriteRune('_')
|
||||
buf2.WriteRune('_')
|
||||
wroteUnderscore = true
|
||||
continue
|
||||
}
|
||||
|
||||
prevOrNextLower := i+1 < len(n) && i-1 > 0 && (unicode.IsLower(n[i+1]) || unicode.IsLower(n[i-1]))
|
||||
if i > 0 && unicode.IsUpper(r) && prevOrNextLower && !wroteUnderscore {
|
||||
buf.WriteRune('_')
|
||||
}
|
||||
|
||||
buf.WriteRune(r)
|
||||
buf2.WriteRune(r)
|
||||
|
||||
wroteUnderscore = false
|
||||
}
|
||||
|
||||
tmp[strings.ToLower(buf.String())] = struct{}{}
|
||||
tmp[strings.ToUpper(buf.String())] = struct{}{}
|
||||
tmp[strings.ToLower(buf2.String())] = struct{}{}
|
||||
tmp[strings.ToUpper(buf2.String())] = struct{}{}
|
||||
}
|
||||
|
||||
for k := range tmp {
|
||||
res = append(res, k)
|
||||
}
|
||||
|
||||
sort.Strings(res)
|
||||
|
||||
return
|
||||
}
|
||||
597
vendor/github.com/vrischmann/envconfig/envconfig_test.go
generated
vendored
Normal file
597
vendor/github.com/vrischmann/envconfig/envconfig_test.go
generated
vendored
Normal file
@@ -0,0 +1,597 @@
|
||||
package envconfig_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/vrischmann/envconfig"
|
||||
)
|
||||
|
||||
func TestParseSimpleConfig(t *testing.T) {
|
||||
var conf struct {
|
||||
Name string
|
||||
Log struct {
|
||||
Path string
|
||||
}
|
||||
}
|
||||
|
||||
err := envconfig.Init(&conf)
|
||||
require.Equal(t, "envconfig: keys NAME, name not found", err.Error())
|
||||
|
||||
os.Setenv("NAME", "foobar")
|
||||
err = envconfig.Init(&conf)
|
||||
require.Equal(t, "envconfig: keys LOG_PATH, log_path not found", err.Error())
|
||||
|
||||
os.Setenv("LOG_PATH", "/var/log/foobar")
|
||||
err = envconfig.Init(&conf)
|
||||
require.Nil(t, err)
|
||||
|
||||
require.Equal(t, "foobar", conf.Name)
|
||||
require.Equal(t, "/var/log/foobar", conf.Log.Path)
|
||||
|
||||
// Clean up at the end of the test - some tests share the same key and we don't values to be seen by those tests
|
||||
os.Setenv("NAME", "")
|
||||
os.Setenv("LOG_PATH", "")
|
||||
}
|
||||
|
||||
func TestParseIntegerConfig(t *testing.T) {
|
||||
var conf struct {
|
||||
Port int
|
||||
Long uint64
|
||||
Version uint8
|
||||
}
|
||||
|
||||
timestamp := time.Now().UnixNano()
|
||||
|
||||
os.Setenv("PORT", "80")
|
||||
os.Setenv("LONG", fmt.Sprintf("%d", timestamp))
|
||||
os.Setenv("VERSION", "2")
|
||||
|
||||
err := envconfig.Init(&conf)
|
||||
require.Nil(t, err)
|
||||
|
||||
require.Equal(t, 80, conf.Port)
|
||||
require.Equal(t, uint64(timestamp), conf.Long)
|
||||
require.Equal(t, uint8(2), conf.Version)
|
||||
}
|
||||
|
||||
func TestParseBoolConfig(t *testing.T) {
|
||||
var conf struct {
|
||||
DoIt bool
|
||||
}
|
||||
|
||||
os.Setenv("DOIT", "true")
|
||||
|
||||
err := envconfig.Init(&conf)
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, true, conf.DoIt)
|
||||
}
|
||||
|
||||
func TestParseBytesConfig(t *testing.T) {
|
||||
var conf struct {
|
||||
Data []byte
|
||||
}
|
||||
|
||||
os.Setenv("DATA", "Rk9PQkFS")
|
||||
|
||||
err := envconfig.Init(&conf)
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, []byte("FOOBAR"), conf.Data)
|
||||
}
|
||||
|
||||
func TestParseFloatConfig(t *testing.T) {
|
||||
var conf struct {
|
||||
Delta float32
|
||||
DeltaV float64
|
||||
}
|
||||
|
||||
os.Setenv("DELTA", "0.02")
|
||||
os.Setenv("DELTAV", "400.20000000001")
|
||||
|
||||
err := envconfig.Init(&conf)
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, float32(0.02), conf.Delta)
|
||||
require.Equal(t, float64(400.20000000001), conf.DeltaV)
|
||||
}
|
||||
|
||||
func TestParseSliceConfig(t *testing.T) {
|
||||
var conf struct {
|
||||
Names []string
|
||||
Ports []int
|
||||
Shards []struct {
|
||||
Name string
|
||||
Addr string
|
||||
}
|
||||
}
|
||||
|
||||
os.Setenv("NAMES", "foobar,barbaz")
|
||||
os.Setenv("PORTS", "900,100")
|
||||
os.Setenv("SHARDS", "{foobar,localhost:2929},{barbaz,localhost:2828}")
|
||||
|
||||
err := envconfig.Init(&conf)
|
||||
require.Nil(t, err)
|
||||
|
||||
require.Equal(t, 2, len(conf.Names))
|
||||
require.Equal(t, "foobar", conf.Names[0])
|
||||
require.Equal(t, "barbaz", conf.Names[1])
|
||||
require.Equal(t, 2, len(conf.Ports))
|
||||
require.Equal(t, 900, conf.Ports[0])
|
||||
require.Equal(t, 100, conf.Ports[1])
|
||||
require.Equal(t, 2, len(conf.Shards))
|
||||
require.Equal(t, "foobar", conf.Shards[0].Name)
|
||||
require.Equal(t, "localhost:2929", conf.Shards[0].Addr)
|
||||
require.Equal(t, "barbaz", conf.Shards[1].Name)
|
||||
require.Equal(t, "localhost:2828", conf.Shards[1].Addr)
|
||||
}
|
||||
|
||||
func TestParseStructSliceWrongData(t *testing.T) {
|
||||
var conf struct {
|
||||
Shards []struct {
|
||||
Name string
|
||||
Addr string
|
||||
}
|
||||
}
|
||||
|
||||
os.Setenv("SHARDS", "foobar")
|
||||
|
||||
err := envconfig.Init(&conf)
|
||||
require.Equal(t, "envconfig: struct token has 1 fields but struct has 2", err.Error())
|
||||
}
|
||||
|
||||
func TestParseStructSliceWrongValue(t *testing.T) {
|
||||
var conf struct {
|
||||
Shards []struct {
|
||||
Name string
|
||||
Port int32
|
||||
}
|
||||
}
|
||||
|
||||
os.Setenv("SHARDS", "{foobar,barbaz}")
|
||||
|
||||
err := envconfig.Init(&conf)
|
||||
require.Equal(t, `strconv.ParseInt: parsing "barbaz": invalid syntax`, err.Error())
|
||||
}
|
||||
|
||||
func TestParseWrongValues(t *testing.T) {
|
||||
var conf struct{ OK bool }
|
||||
os.Setenv("OK", "foobar")
|
||||
err := envconfig.Init(&conf)
|
||||
require.Equal(t, `strconv.ParseBool: parsing "foobar": invalid syntax`, err.Error())
|
||||
|
||||
var conf2 struct{ Port int }
|
||||
os.Setenv("PORT", "foobar")
|
||||
err = envconfig.Init(&conf2)
|
||||
require.Equal(t, `strconv.ParseInt: parsing "foobar": invalid syntax`, err.Error())
|
||||
|
||||
var conf3 struct{ Port uint }
|
||||
os.Setenv("PORT", "foobar")
|
||||
err = envconfig.Init(&conf3)
|
||||
require.Equal(t, `strconv.ParseUint: parsing "foobar": invalid syntax`, err.Error())
|
||||
|
||||
var conf4 struct{ Port float32 }
|
||||
os.Setenv("PORT", "foobar")
|
||||
err = envconfig.Init(&conf4)
|
||||
require.Equal(t, `strconv.ParseFloat: parsing "foobar": invalid syntax`, err.Error())
|
||||
|
||||
var conf5 struct{ Data []byte }
|
||||
os.Setenv("DATA", "foobar")
|
||||
err = envconfig.Init(&conf5)
|
||||
require.Equal(t, "illegal base64 data at input byte 4", err.Error())
|
||||
}
|
||||
|
||||
func TestDurationConfig(t *testing.T) {
|
||||
var conf struct {
|
||||
Timeout time.Duration
|
||||
}
|
||||
|
||||
os.Setenv("TIMEOUT", "1m")
|
||||
|
||||
err := envconfig.Init(&conf)
|
||||
require.Nil(t, err)
|
||||
|
||||
require.Equal(t, time.Minute*1, conf.Timeout)
|
||||
}
|
||||
|
||||
func TestInvalidDurationConfig(t *testing.T) {
|
||||
var conf struct {
|
||||
Timeout time.Duration
|
||||
}
|
||||
|
||||
os.Setenv("TIMEOUT", "foo")
|
||||
|
||||
err := envconfig.Init(&conf)
|
||||
require.NotNil(t, err)
|
||||
}
|
||||
|
||||
func TestAllPointerConfig(t *testing.T) {
|
||||
var conf struct {
|
||||
Name *string
|
||||
Port *int
|
||||
Delta *float32
|
||||
DeltaV *float64
|
||||
Hosts *[]string
|
||||
Shards *[]*struct {
|
||||
Name *string
|
||||
Addr *string
|
||||
}
|
||||
Master *struct {
|
||||
Name *string
|
||||
Addr *string
|
||||
}
|
||||
Timeout *time.Duration
|
||||
}
|
||||
|
||||
os.Setenv("NAME", "foobar")
|
||||
os.Setenv("PORT", "9000")
|
||||
os.Setenv("DELTA", "40.01")
|
||||
os.Setenv("DELTAV", "200.00001")
|
||||
os.Setenv("HOSTS", "localhost,free.fr")
|
||||
os.Setenv("SHARDS", "{foobar,localhost:2828},{barbaz,localhost:2929}")
|
||||
os.Setenv("MASTER_NAME", "master")
|
||||
os.Setenv("MASTER_ADDR", "localhost:2727")
|
||||
os.Setenv("TIMEOUT", "1m")
|
||||
|
||||
err := envconfig.Init(&conf)
|
||||
require.Nil(t, err)
|
||||
|
||||
require.Equal(t, "foobar", *conf.Name)
|
||||
require.Equal(t, 9000, *conf.Port)
|
||||
require.Equal(t, float32(40.01), *conf.Delta)
|
||||
require.Equal(t, 200.00001, *conf.DeltaV)
|
||||
require.Equal(t, 2, len(*conf.Hosts))
|
||||
require.Equal(t, "localhost", (*conf.Hosts)[0])
|
||||
require.Equal(t, "free.fr", (*conf.Hosts)[1])
|
||||
require.Equal(t, 2, len(*conf.Shards))
|
||||
require.Equal(t, "foobar", *(*conf.Shards)[0].Name)
|
||||
require.Equal(t, "localhost:2828", *(*conf.Shards)[0].Addr)
|
||||
require.Equal(t, "barbaz", *(*conf.Shards)[1].Name)
|
||||
require.Equal(t, "localhost:2929", *(*conf.Shards)[1].Addr)
|
||||
require.Equal(t, "master", *conf.Master.Name)
|
||||
require.Equal(t, "localhost:2727", *conf.Master.Addr)
|
||||
require.Equal(t, time.Minute*1, *conf.Timeout)
|
||||
}
|
||||
|
||||
type logMode uint
|
||||
|
||||
const (
|
||||
logFile logMode = iota + 1
|
||||
logStdout
|
||||
)
|
||||
|
||||
func (m *logMode) Unmarshal(s string) error {
|
||||
switch strings.ToLower(s) {
|
||||
case "file":
|
||||
*m = logFile
|
||||
case "stdout":
|
||||
*m = logStdout
|
||||
default:
|
||||
return fmt.Errorf("unable to unmarshal %s", s)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestUnmarshaler(t *testing.T) {
|
||||
var conf struct {
|
||||
LogMode logMode
|
||||
}
|
||||
|
||||
os.Setenv("LOGMODE", "file")
|
||||
|
||||
err := envconfig.Init(&conf)
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, logFile, conf.LogMode)
|
||||
|
||||
var conf2 struct {
|
||||
LogMode *logMode
|
||||
}
|
||||
|
||||
err = envconfig.Init(&conf2)
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, logFile, *conf2.LogMode)
|
||||
}
|
||||
|
||||
func TestParseOptionalConfig(t *testing.T) {
|
||||
var conf struct {
|
||||
Name string `envconfig:"optional"`
|
||||
Flag bool `envconfig:"optional"`
|
||||
Timeout time.Duration `envconfig:"optional"`
|
||||
Port int `envconfig:"optional"`
|
||||
Port2 uint `envconfig:"optional"`
|
||||
Delta float32 `envconfig:"optional"`
|
||||
DeltaV float64 `envconfig:"optional"`
|
||||
Slice []string `envconfig:"optional"`
|
||||
Struct struct {
|
||||
A string
|
||||
B int
|
||||
} `envconfig:"optional"`
|
||||
}
|
||||
|
||||
os.Setenv("NAME", "")
|
||||
os.Setenv("FLAG", "")
|
||||
os.Setenv("TIMEOUT", "")
|
||||
os.Setenv("PORT", "")
|
||||
os.Setenv("PORT2", "")
|
||||
os.Setenv("DELTA", "")
|
||||
os.Setenv("DELTAV", "")
|
||||
os.Setenv("SLICE", "")
|
||||
os.Setenv("STRUCT_A", "")
|
||||
|
||||
err := envconfig.Init(&conf)
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, "", conf.Name)
|
||||
|
||||
os.Setenv("NAME", "foobar")
|
||||
os.Setenv("SLICE", "a,b")
|
||||
os.Setenv("STRUCT_A", "foobar")
|
||||
os.Setenv("STRUCT_B", "1")
|
||||
|
||||
err = envconfig.Init(&conf)
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, "foobar", conf.Name)
|
||||
require.Equal(t, []string{"a", "b"}, conf.Slice)
|
||||
require.Equal(t, "foobar", conf.Struct.A)
|
||||
require.Equal(t, 1, conf.Struct.B)
|
||||
}
|
||||
|
||||
func TestParseSkippableConfig(t *testing.T) {
|
||||
var conf struct {
|
||||
Flag bool `envconfig:"-"`
|
||||
}
|
||||
|
||||
os.Setenv("FLAG", "true")
|
||||
|
||||
err := envconfig.Init(&conf)
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, false, conf.Flag)
|
||||
}
|
||||
|
||||
func TestParseCustomNameConfig(t *testing.T) {
|
||||
var conf struct {
|
||||
Name string `envconfig:"customName"`
|
||||
}
|
||||
|
||||
os.Setenv("customName", "foobar")
|
||||
|
||||
err := envconfig.Init(&conf)
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, "foobar", conf.Name)
|
||||
}
|
||||
|
||||
func TestParseOptionalStruct(t *testing.T) {
|
||||
var conf struct {
|
||||
Master struct {
|
||||
Name string
|
||||
} `envconfig:"optional"`
|
||||
}
|
||||
|
||||
os.Setenv("MASTER_NAME", "")
|
||||
|
||||
err := envconfig.Init(&conf)
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, "", conf.Master.Name)
|
||||
}
|
||||
|
||||
func TestParsePrefixedStruct(t *testing.T) {
|
||||
var conf struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
os.Setenv("NAME", "")
|
||||
os.Setenv("FOO_NAME", "")
|
||||
|
||||
os.Setenv("NAME", "bad")
|
||||
err := envconfig.InitWithPrefix(&conf, "FOO")
|
||||
require.NotNil(t, err)
|
||||
|
||||
os.Setenv("FOO_NAME", "good")
|
||||
err = envconfig.InitWithPrefix(&conf, "FOO")
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, "good", conf.Name)
|
||||
}
|
||||
|
||||
func TestUnexportedField(t *testing.T) {
|
||||
var conf struct {
|
||||
name string
|
||||
}
|
||||
|
||||
os.Setenv("NAME", "foobar")
|
||||
|
||||
err := envconfig.Init(&conf)
|
||||
require.Equal(t, envconfig.ErrUnexportedField, err)
|
||||
|
||||
err = envconfig.InitWithOptions(&conf, envconfig.Options{AllowUnexported: true})
|
||||
require.Equal(t, nil, err)
|
||||
}
|
||||
|
||||
func TestNestedUnexportedField(t *testing.T) {
|
||||
var conf struct {
|
||||
Foo struct {
|
||||
Bar struct {
|
||||
baz string
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
os.Setenv("FOO_BAR_BAZ", "foobar")
|
||||
|
||||
err := envconfig.Init(&conf)
|
||||
require.Equal(t, envconfig.ErrUnexportedField, err)
|
||||
|
||||
err = envconfig.InitWithOptions(&conf, envconfig.Options{AllowUnexported: true})
|
||||
require.Equal(t, nil, err)
|
||||
}
|
||||
|
||||
type sliceWithUnmarshaler []int
|
||||
|
||||
func (sl *sliceWithUnmarshaler) Unmarshal(s string) error {
|
||||
tokens := strings.Split(s, ".")
|
||||
for _, tok := range tokens {
|
||||
tmp, err := strconv.Atoi(tok)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*sl = append(*sl, tmp)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestSliceTypeWithUnmarshaler(t *testing.T) {
|
||||
var conf struct {
|
||||
Data sliceWithUnmarshaler
|
||||
}
|
||||
|
||||
os.Setenv("DATA", "1.2.3")
|
||||
|
||||
err := envconfig.Init(&conf)
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, 3, len(conf.Data))
|
||||
require.Equal(t, 1, conf.Data[0])
|
||||
require.Equal(t, 2, conf.Data[1])
|
||||
require.Equal(t, 3, conf.Data[2])
|
||||
}
|
||||
|
||||
func TestParseDefaultVal(t *testing.T) {
|
||||
var conf struct {
|
||||
MySQL struct {
|
||||
Master struct {
|
||||
Address string `envconfig:"default=localhost"`
|
||||
Port int `envconfig:"default=3306"`
|
||||
}
|
||||
Timeout time.Duration `envconfig:"default=1m,myTimeout"`
|
||||
LocalTimeout time.Duration `envconfig:"myTimeout2,default=1m"`
|
||||
}
|
||||
}
|
||||
|
||||
err := envconfig.Init(&conf)
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, "localhost", conf.MySQL.Master.Address)
|
||||
require.Equal(t, 3306, conf.MySQL.Master.Port)
|
||||
require.Equal(t, time.Minute*1, conf.MySQL.Timeout)
|
||||
|
||||
os.Setenv("myTimeout", "2m")
|
||||
os.Setenv("myTimeout2", "20m")
|
||||
|
||||
err = envconfig.Init(&conf)
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, "localhost", conf.MySQL.Master.Address)
|
||||
require.Equal(t, 3306, conf.MySQL.Master.Port)
|
||||
require.Equal(t, time.Minute*2, conf.MySQL.Timeout)
|
||||
require.Equal(t, time.Minute*20, conf.MySQL.LocalTimeout)
|
||||
}
|
||||
|
||||
func TestDefaultSlice(t *testing.T) {
|
||||
// See https://github.com/vrischmann/envconfig/pull/15
|
||||
//
|
||||
// The way people think about the following default value, is that the slice will be [a,b]
|
||||
// However this never worked because we split the entire envconfig tag on , therefore default is just `a` here.
|
||||
// The proper thing to do is to introduce a new format in the tag that doesn't have this limitation, but we don't have that yet.
|
||||
// For now, we simply return an error indicating default is not unsupported on slices.
|
||||
|
||||
var conf struct {
|
||||
Hosts []string `envconfig:"default=a,b"`
|
||||
}
|
||||
|
||||
err := envconfig.Init(&conf)
|
||||
require.NotNil(t, err)
|
||||
require.Equal(t, envconfig.ErrDefaultUnsupportedOnSlice, err)
|
||||
}
|
||||
|
||||
func TestInitNotAPointer(t *testing.T) {
|
||||
err := envconfig.Init("foobar")
|
||||
require.Equal(t, envconfig.ErrNotAPointer, err)
|
||||
}
|
||||
|
||||
func TestInitPointerToAPointer(t *testing.T) {
|
||||
type Conf struct {
|
||||
Name string
|
||||
}
|
||||
var tmp *Conf
|
||||
|
||||
os.Setenv("NAME", "foobar")
|
||||
|
||||
err := envconfig.Init(&tmp)
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, "foobar", tmp.Name)
|
||||
}
|
||||
|
||||
func TestInitInvalidValueKind(t *testing.T) {
|
||||
sl := []string{"foo", "bar"}
|
||||
err := envconfig.Init(&sl)
|
||||
require.Equal(t, envconfig.ErrInvalidValueKind, err)
|
||||
}
|
||||
|
||||
func TestInvalidFieldValueKind(t *testing.T) {
|
||||
var conf struct {
|
||||
Foo interface{}
|
||||
}
|
||||
|
||||
os.Setenv("FOO", "lalala")
|
||||
|
||||
err := envconfig.Init(&conf)
|
||||
require.Equal(t, "envconfig: kind interface not supported", err.Error())
|
||||
}
|
||||
|
||||
func TestInvalidSliceElementValueKind(t *testing.T) {
|
||||
var conf struct {
|
||||
Foo []interface{}
|
||||
}
|
||||
|
||||
os.Setenv("FOO", "lalala")
|
||||
|
||||
err := envconfig.Init(&conf)
|
||||
require.Equal(t, "envconfig: kind interface not supported", err.Error())
|
||||
}
|
||||
|
||||
func TestParseEmptyTag(t *testing.T) {
|
||||
var conf struct {
|
||||
Name string `envconfig:""`
|
||||
}
|
||||
|
||||
os.Setenv("NAME", "foobar")
|
||||
|
||||
err := envconfig.Init(&conf)
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, "foobar", conf.Name)
|
||||
}
|
||||
|
||||
func TestLeaveNil(t *testing.T) {
|
||||
var conf struct {
|
||||
MySQL *struct {
|
||||
Name string
|
||||
}
|
||||
}
|
||||
|
||||
err := envconfig.InitWithOptions(&conf, envconfig.Options{
|
||||
AllOptional: true,
|
||||
LeaveNil: true,
|
||||
})
|
||||
require.Nil(t, err)
|
||||
require.Nil(t, conf.MySQL)
|
||||
}
|
||||
|
||||
type myMapType map[string]int
|
||||
|
||||
func (t *myMapType) Unmarshal(s string) error {
|
||||
(*t)[s] = 1
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestParseMapType(t *testing.T) {
|
||||
var conf struct {
|
||||
Map myMapType
|
||||
}
|
||||
|
||||
os.Setenv("MAP", "a")
|
||||
|
||||
err := envconfig.Init(&conf)
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, 1, conf.Map["a"])
|
||||
}
|
||||
89
vendor/github.com/vrischmann/envconfig/example_test.go
generated
vendored
Normal file
89
vendor/github.com/vrischmann/envconfig/example_test.go
generated
vendored
Normal file
@@ -0,0 +1,89 @@
|
||||
package envconfig_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/vrischmann/envconfig"
|
||||
)
|
||||
|
||||
func ExampleInit() {
|
||||
var conf struct {
|
||||
MySQL struct {
|
||||
Host string
|
||||
Port int
|
||||
Database struct {
|
||||
User string
|
||||
Password string
|
||||
Name string
|
||||
}
|
||||
Params struct {
|
||||
Charset string `envconfig:"-"`
|
||||
}
|
||||
}
|
||||
Log struct {
|
||||
Path string `envconfig:"default=/var/log/mylog.log"`
|
||||
Rotate bool `envconfig:"logRotate"`
|
||||
}
|
||||
NbWorkers int
|
||||
Timeout time.Duration
|
||||
Cassandra struct {
|
||||
SSLCert string
|
||||
SSLKey string
|
||||
}
|
||||
}
|
||||
|
||||
os.Setenv("MYSQL_HOST", "localhost")
|
||||
os.Setenv("MYSQL_PORT", "3306")
|
||||
os.Setenv("MYSQL_DATABASE_USER", "root")
|
||||
os.Setenv("MYSQL_DATABASE_PASSWORD", "foobar")
|
||||
os.Setenv("MYSQL_DATABASE_NAME", "default")
|
||||
os.Setenv("logRotate", "true")
|
||||
os.Setenv("NBWORKERS", "10")
|
||||
os.Setenv("TIMEOUT", "120s")
|
||||
os.Setenv("CASSANDRA_SSL_CERT", "/etc/cassandra/ssl.crt")
|
||||
os.Setenv("CASSANDRA_SSL_KEY", "/etc/cassandra/ssl.key")
|
||||
|
||||
if err := envconfig.Init(&conf); err != nil {
|
||||
fmt.Printf("err=%s\n", err)
|
||||
}
|
||||
|
||||
fmt.Println(conf.MySQL.Database.User)
|
||||
fmt.Println(conf.Log.Rotate)
|
||||
fmt.Println(conf.Timeout)
|
||||
fmt.Println(conf.Log.Path)
|
||||
fmt.Println(conf.Cassandra.SSLCert)
|
||||
fmt.Println(conf.Cassandra.SSLKey)
|
||||
// Output:
|
||||
// root
|
||||
// true
|
||||
// 2m0s
|
||||
// /var/log/mylog.log
|
||||
// /etc/cassandra/ssl.crt
|
||||
// /etc/cassandra/ssl.key
|
||||
}
|
||||
|
||||
func ExampleInitWithPrefix() {
|
||||
var conf struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
os.Setenv("NAME", "")
|
||||
os.Setenv("FOO_NAME", "")
|
||||
|
||||
os.Setenv("NAME", "foobar")
|
||||
|
||||
err := envconfig.InitWithPrefix(&conf, "FOO")
|
||||
fmt.Println(err)
|
||||
|
||||
os.Setenv("FOO_NAME", "foobar")
|
||||
err = envconfig.InitWithPrefix(&conf, "FOO")
|
||||
fmt.Println(err)
|
||||
|
||||
fmt.Println(conf.Name)
|
||||
// Output:
|
||||
// envconfig: keys FOO_NAME, foo_name not found
|
||||
// <nil>
|
||||
// foobar
|
||||
}
|
||||
62
vendor/github.com/vrischmann/envconfig/keys_test.go
generated
vendored
Normal file
62
vendor/github.com/vrischmann/envconfig/keys_test.go
generated
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
package envconfig
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestMakeAllPossibleKeys(t *testing.T) {
|
||||
fieldName := "CassandraSslCert"
|
||||
keys := makeAllPossibleKeys(&context{
|
||||
name: fieldName,
|
||||
})
|
||||
|
||||
require.Equal(t, 4, len(keys))
|
||||
require.Equal(t, "CASSANDRASSLCERT", keys[0])
|
||||
require.Equal(t, "CASSANDRA_SSL_CERT", keys[1])
|
||||
require.Equal(t, "cassandra_ssl_cert", keys[2])
|
||||
require.Equal(t, "cassandrasslcert", keys[3])
|
||||
|
||||
fieldName = "CassandraSSLCert"
|
||||
keys = makeAllPossibleKeys(&context{
|
||||
name: fieldName,
|
||||
})
|
||||
|
||||
require.Equal(t, 4, len(keys))
|
||||
require.Equal(t, "CASSANDRASSLCERT", keys[0])
|
||||
require.Equal(t, "CASSANDRA_SSL_CERT", keys[1])
|
||||
require.Equal(t, "cassandra_ssl_cert", keys[2])
|
||||
require.Equal(t, "cassandrasslcert", keys[3])
|
||||
|
||||
fieldName = "Cassandra.SslCert"
|
||||
keys = makeAllPossibleKeys(&context{
|
||||
name: fieldName,
|
||||
})
|
||||
|
||||
require.Equal(t, 4, len(keys))
|
||||
require.Equal(t, "CASSANDRA_SSLCERT", keys[0])
|
||||
require.Equal(t, "CASSANDRA_SSL_CERT", keys[1])
|
||||
require.Equal(t, "cassandra_ssl_cert", keys[2])
|
||||
require.Equal(t, "cassandra_sslcert", keys[3])
|
||||
|
||||
fieldName = "Cassandra.SSLCert"
|
||||
keys = makeAllPossibleKeys(&context{
|
||||
name: fieldName,
|
||||
})
|
||||
|
||||
require.Equal(t, 4, len(keys))
|
||||
require.Equal(t, "CASSANDRA_SSLCERT", keys[0])
|
||||
require.Equal(t, "CASSANDRA_SSL_CERT", keys[1])
|
||||
require.Equal(t, "cassandra_ssl_cert", keys[2])
|
||||
require.Equal(t, "cassandra_sslcert", keys[3])
|
||||
|
||||
fieldName = "Name"
|
||||
keys = makeAllPossibleKeys(&context{
|
||||
name: fieldName,
|
||||
})
|
||||
|
||||
require.Equal(t, 2, len(keys))
|
||||
require.Equal(t, "NAME", keys[0])
|
||||
require.Equal(t, "name", keys[1])
|
||||
}
|
||||
76
vendor/github.com/vrischmann/envconfig/slice.go
generated
vendored
Normal file
76
vendor/github.com/vrischmann/envconfig/slice.go
generated
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
package envconfig
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type sliceTokenizer struct {
|
||||
err error
|
||||
r *bufio.Reader
|
||||
buf bytes.Buffer
|
||||
inBraces bool
|
||||
}
|
||||
|
||||
var eof = rune(0)
|
||||
|
||||
func newSliceTokenizer(str string) *sliceTokenizer {
|
||||
return &sliceTokenizer{
|
||||
r: bufio.NewReader(strings.NewReader(str)),
|
||||
}
|
||||
}
|
||||
|
||||
func (t *sliceTokenizer) scan() bool {
|
||||
for {
|
||||
if t.err == io.EOF && t.buf.Len() == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
ch := t.readRune()
|
||||
if ch == eof {
|
||||
return true
|
||||
}
|
||||
|
||||
if ch == '{' {
|
||||
t.inBraces = true
|
||||
}
|
||||
if ch == '}' {
|
||||
t.inBraces = false
|
||||
}
|
||||
|
||||
if ch == ',' && !t.inBraces {
|
||||
return true
|
||||
}
|
||||
|
||||
// NOTE(vincent): we ignore the WriteRune error here because there is NO WAY
|
||||
// for WriteRune to return an error.
|
||||
// Yep. Seriously. Look here http://golang.org/src/bytes/buffer.go?s=7661:7714#L227
|
||||
_, _ = t.buf.WriteRune(ch)
|
||||
}
|
||||
}
|
||||
|
||||
func (t *sliceTokenizer) readRune() rune {
|
||||
ch, _, err := t.r.ReadRune()
|
||||
if err != nil {
|
||||
t.err = err
|
||||
return eof
|
||||
}
|
||||
|
||||
return ch
|
||||
}
|
||||
|
||||
func (t *sliceTokenizer) text() string {
|
||||
str := t.buf.String()
|
||||
t.buf.Reset()
|
||||
|
||||
return str
|
||||
}
|
||||
|
||||
func (t *sliceTokenizer) Err() error {
|
||||
if t.err == io.EOF {
|
||||
return nil
|
||||
}
|
||||
return t.err
|
||||
}
|
||||
47
vendor/github.com/vrischmann/envconfig/slice_test.go
generated
vendored
Normal file
47
vendor/github.com/vrischmann/envconfig/slice_test.go
generated
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
package envconfig
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestSliceTokenizer(t *testing.T) {
|
||||
str := "foobar,barbaz"
|
||||
tnz := newSliceTokenizer(str)
|
||||
|
||||
b := tnz.scan()
|
||||
require.Nil(t, tnz.Err())
|
||||
require.Equal(t, true, b)
|
||||
|
||||
require.Equal(t, "foobar", tnz.text())
|
||||
|
||||
b = tnz.scan()
|
||||
require.Nil(t, tnz.Err())
|
||||
require.Equal(t, true, b)
|
||||
require.Equal(t, "barbaz", tnz.text())
|
||||
|
||||
b = tnz.scan()
|
||||
require.Nil(t, tnz.Err())
|
||||
require.Equal(t, false, b)
|
||||
}
|
||||
|
||||
func TestSliceOfStructsTokenizer(t *testing.T) {
|
||||
str := "{foobar,100},{barbaz,200}"
|
||||
tnz := newSliceTokenizer(str)
|
||||
|
||||
b := tnz.scan()
|
||||
require.Nil(t, tnz.Err())
|
||||
require.Equal(t, true, b)
|
||||
|
||||
require.Equal(t, "{foobar,100}", tnz.text())
|
||||
|
||||
b = tnz.scan()
|
||||
require.Nil(t, tnz.Err())
|
||||
require.Equal(t, true, b)
|
||||
require.Equal(t, "{barbaz,200}", tnz.text())
|
||||
|
||||
b = tnz.scan()
|
||||
require.Nil(t, tnz.Err())
|
||||
require.Equal(t, false, b)
|
||||
}
|
||||
Reference in New Issue
Block a user