Use env lib, add PROXY_PORT ENV variable
This commit is contained in:
parent
803767b6f7
commit
678a8cc192
8
Gopkg.lock
generated
8
Gopkg.lock
generated
@ -7,6 +7,12 @@
|
|||||||
packages = ["."]
|
packages = ["."]
|
||||||
revision = "e75332964ef517daa070d7c38a9466a0d687e0a5"
|
revision = "e75332964ef517daa070d7c38a9466a0d687e0a5"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "github.com/caarlos0/env"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "3e0f30cbf50bb82273cf60e7bb70869f4852e9b8"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
name = "golang.org/x/net"
|
name = "golang.org/x/net"
|
||||||
@ -16,6 +22,6 @@
|
|||||||
[solve-meta]
|
[solve-meta]
|
||||||
analyzer-name = "dep"
|
analyzer-name = "dep"
|
||||||
analyzer-version = 1
|
analyzer-version = 1
|
||||||
inputs-digest = "bb1b87ff840d0a559073f327d2c493d6ca2c37ed971b13eef6796925f3980a7a"
|
inputs-digest = "b579eb59bf4c9ca68a34a1b121d49796b87e80c2514576b22c53c4507f129c87"
|
||||||
solver-name = "gps-cdcl"
|
solver-name = "gps-cdcl"
|
||||||
solver-version = 1
|
solver-version = 1
|
||||||
|
@ -24,3 +24,7 @@
|
|||||||
[[constraint]]
|
[[constraint]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
name = "github.com/armon/go-socks5"
|
name = "github.com/armon/go-socks5"
|
||||||
|
|
||||||
|
[[constraint]]
|
||||||
|
branch = "master"
|
||||||
|
name = "github.com/caarlos0/env"
|
29
server.go
29
server.go
@ -1,34 +1,47 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/armon/go-socks5"
|
"github.com/armon/go-socks5"
|
||||||
|
"github.com/caarlos0/env"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
type params struct {
|
||||||
|
User string `env:"PROXY_USER" envDefault:""`
|
||||||
|
Password string `env:"PROXY_PASSWORD" envDefault:""`
|
||||||
|
Port string `env:"PROXY_PORT" envDefault:"1080"`
|
||||||
|
}
|
||||||
|
|
||||||
conf := &socks5.Config{
|
func main() {
|
||||||
|
// Working with app params
|
||||||
|
cfg := params{}
|
||||||
|
err := env.Parse(&cfg)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("%+v\n", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
//Initialize socks5 config
|
||||||
|
socsk5conf := &socks5.Config{
|
||||||
Logger: log.New(os.Stdout, "", log.LstdFlags),
|
Logger: log.New(os.Stdout, "", log.LstdFlags),
|
||||||
}
|
}
|
||||||
|
|
||||||
if os.Getenv("PROXY_USER")+os.Getenv("PROXY_PASSWORD") != "" {
|
if cfg.User+cfg.Password != "" {
|
||||||
creds := socks5.StaticCredentials{
|
creds := socks5.StaticCredentials{
|
||||||
os.Getenv("PROXY_USER"): os.Getenv("PROXY_PASSWORD"),
|
os.Getenv("PROXY_USER"): os.Getenv("PROXY_PASSWORD"),
|
||||||
}
|
}
|
||||||
cator := socks5.UserPassAuthenticator{Credentials: creds}
|
cator := socks5.UserPassAuthenticator{Credentials: creds}
|
||||||
conf.AuthMethods = []socks5.Authenticator{cator}
|
socsk5conf.AuthMethods = []socks5.Authenticator{cator}
|
||||||
}
|
}
|
||||||
|
|
||||||
server, err := socks5.New(conf)
|
server, err := socks5.New(socsk5conf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println("Start listening ...")
|
log.Printf("Start listening proxy service on port %s\n", cfg.Port)
|
||||||
if err := server.ListenAndServe("tcp", ":1080"); err != nil {
|
if err := server.ListenAndServe("tcp", ":"+cfg.Port); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
1
vendor/github.com/caarlos0/env/.gitignore
generated
vendored
Normal file
1
vendor/github.com/caarlos0/env/.gitignore
generated
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
coverage.out
|
2
vendor/github.com/caarlos0/env/.hound.yml
generated
vendored
Normal file
2
vendor/github.com/caarlos0/env/.hound.yml
generated
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
go:
|
||||||
|
enabled: true
|
16
vendor/github.com/caarlos0/env/.travis.yml
generated
vendored
Normal file
16
vendor/github.com/caarlos0/env/.travis.yml
generated
vendored
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
language: go
|
||||||
|
go:
|
||||||
|
- 1.5
|
||||||
|
- 1.6
|
||||||
|
- 1.7
|
||||||
|
- 1.8
|
||||||
|
- tip
|
||||||
|
before_install:
|
||||||
|
- go get github.com/axw/gocov/gocov
|
||||||
|
- go get github.com/mattn/goveralls
|
||||||
|
- go get golang.org/x/tools/cmd/cover
|
||||||
|
script:
|
||||||
|
- go test -v -cover -race -coverprofile=coverage.out
|
||||||
|
after_script:
|
||||||
|
- go get github.com/mattn/goveralls
|
||||||
|
- goveralls -coverprofile=coverage.out -service=travis-ci -repotoken='eCcizKmTdSaJCz8Ih33WDppdqb9kioYwi'
|
21
vendor/github.com/caarlos0/env/LICENSE.md
generated
vendored
Normal file
21
vendor/github.com/caarlos0/env/LICENSE.md
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2015-2016 Carlos Alexandro Becker
|
||||||
|
|
||||||
|
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.
|
115
vendor/github.com/caarlos0/env/README.md
generated
vendored
Normal file
115
vendor/github.com/caarlos0/env/README.md
generated
vendored
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
# env [![Build Status](https://travis-ci.org/caarlos0/env.svg?branch=master)](https://travis-ci.org/caarlos0/env) [![Coverage Status](https://coveralls.io/repos/caarlos0/env/badge.svg?branch=master&service=github)](https://coveralls.io/github/caarlos0/env?branch=master) [![](https://godoc.org/github.com/caarlos0/env?status.svg)](http://godoc.org/github.com/caarlos0/env) [![](http://goreportcard.com/badge/caarlos0/env)](http://goreportcard.com/report/caarlos0/env) [![SayThanks.io](https://img.shields.io/badge/SayThanks.io-%E2%98%BC-1EAEDB.svg?style=flat-square)](https://saythanks.io/to/caarlos0)
|
||||||
|
|
||||||
|
A KISS way to deal with environment variables in Go.
|
||||||
|
|
||||||
|
## Why
|
||||||
|
|
||||||
|
At first, it was boring for me to write down an entire function just to
|
||||||
|
get some `var` from the environment and default to another in case it's missing.
|
||||||
|
|
||||||
|
For that manner, I wrote a `GetOr` function in the
|
||||||
|
[go-idioms](https://github.com/caarlos0/go-idioms) project.
|
||||||
|
|
||||||
|
Then, I got pissed about writing `os.Getenv`, `os.Setenv`, `os.Unsetenv`...
|
||||||
|
it kind of make more sense to me write it as `env.Get`, `env.Set`, `env.Unset`.
|
||||||
|
So I did.
|
||||||
|
|
||||||
|
Then I got a better idea: to use `struct` tags to do all that work for me.
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
A very basic example (check the `examples` folder):
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/caarlos0/env"
|
||||||
|
)
|
||||||
|
|
||||||
|
type config struct {
|
||||||
|
Home string `env:"HOME"`
|
||||||
|
Port int `env:"PORT" envDefault:"3000"`
|
||||||
|
IsProduction bool `env:"PRODUCTION"`
|
||||||
|
Hosts []string `env:"HOSTS" envSeparator:":"`
|
||||||
|
Duration time.Duration `env:"DURATION"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
cfg := config{}
|
||||||
|
err := env.Parse(&cfg)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("%+v\n", err)
|
||||||
|
}
|
||||||
|
fmt.Printf("%+v\n", cfg)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
You can run it like this:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
$ PRODUCTION=true HOSTS="host1:host2:host3" DURATION=1s go run examples/first.go
|
||||||
|
{Home:/your/home Port:3000 IsProduction:true Hosts:[host1 host2 host3] Duration:1s}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Supported types and defaults
|
||||||
|
|
||||||
|
The library has built-in support for the following types:
|
||||||
|
|
||||||
|
* `string`
|
||||||
|
* `int`
|
||||||
|
* `uint`
|
||||||
|
* `int64`
|
||||||
|
* `bool`
|
||||||
|
* `float32`
|
||||||
|
* `float64`
|
||||||
|
* `time.Duration`
|
||||||
|
* `[]string`
|
||||||
|
* `[]int`
|
||||||
|
* `[]bool`
|
||||||
|
* `[]float32`
|
||||||
|
* `[]float64`
|
||||||
|
* `[]time.Duration`
|
||||||
|
* .. or use/define a [custom parser func](#custom-parser-funcs) for any other type
|
||||||
|
|
||||||
|
If you set the `envDefault` tag for something, this value will be used in the
|
||||||
|
case of absence of it in the environment. If you don't do that AND the
|
||||||
|
environment variable is also not set, the zero-value
|
||||||
|
of the type will be used: empty for `string`s, `false` for `bool`s
|
||||||
|
and `0` for `int`s.
|
||||||
|
|
||||||
|
By default, slice types will split the environment value on `,`; you can change this behavior by setting the `envSeparator` tag.
|
||||||
|
|
||||||
|
## Custom Parser Funcs
|
||||||
|
|
||||||
|
If you have a type that is not supported out of the box by the lib, you are able
|
||||||
|
to use (or define) and pass custom parsers (and their associated `reflect.Type`) to the
|
||||||
|
`env.ParseWithFuncs()` function.
|
||||||
|
|
||||||
|
In addition to accepting a struct pointer (same as `Parse()`), this function also
|
||||||
|
accepts a `env.CustomParsers` arg that under the covers is a `map[reflect.Type]env.ParserFunc`.
|
||||||
|
|
||||||
|
To see what this looks like in practice, take a look at the [commented block in the example](https://github.com/caarlos0/env/blob/master/examples/first.go#L35-L39).
|
||||||
|
|
||||||
|
`env` also ships with some pre-built custom parser funcs for common types. You
|
||||||
|
can check them out [here](parsers/).
|
||||||
|
|
||||||
|
## Required fields
|
||||||
|
|
||||||
|
The `env` tag option `required` (e.g., `env:"tagKey,required"`) can be added
|
||||||
|
to ensure that some environment variable is set. In the example above,
|
||||||
|
an error is returned if the `config` struct is changed to:
|
||||||
|
|
||||||
|
|
||||||
|
```go
|
||||||
|
type config struct {
|
||||||
|
Home string `env:"HOME"`
|
||||||
|
Port int `env:"PORT" envDefault:"3000"`
|
||||||
|
IsProduction bool `env:"PRODUCTION"`
|
||||||
|
Hosts []string `env:"HOSTS" envSeparator:":"`
|
||||||
|
SecretKey string `env:"SECRET_KEY,required"`
|
||||||
|
}
|
||||||
|
```
|
398
vendor/github.com/caarlos0/env/env.go
generated
vendored
Normal file
398
vendor/github.com/caarlos0/env/env.go
generated
vendored
Normal file
@ -0,0 +1,398 @@
|
|||||||
|
package env
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrNotAStructPtr is returned if you pass something that is not a pointer to a
|
||||||
|
// Struct to Parse
|
||||||
|
ErrNotAStructPtr = errors.New("Expected a pointer to a Struct")
|
||||||
|
// ErrUnsupportedType if the struct field type is not supported by env
|
||||||
|
ErrUnsupportedType = errors.New("Type is not supported")
|
||||||
|
// ErrUnsupportedSliceType if the slice element type is not supported by env
|
||||||
|
ErrUnsupportedSliceType = errors.New("Unsupported slice type")
|
||||||
|
// OnEnvVarSet is an optional convenience callback, such as for logging purposes.
|
||||||
|
// If not nil, it's called after successfully setting the given field from the given value.
|
||||||
|
OnEnvVarSet func(reflect.StructField, string)
|
||||||
|
// Friendly names for reflect types
|
||||||
|
sliceOfInts = reflect.TypeOf([]int(nil))
|
||||||
|
sliceOfInt64s = reflect.TypeOf([]int64(nil))
|
||||||
|
sliceOfUint64s = reflect.TypeOf([]uint64(nil))
|
||||||
|
sliceOfStrings = reflect.TypeOf([]string(nil))
|
||||||
|
sliceOfBools = reflect.TypeOf([]bool(nil))
|
||||||
|
sliceOfFloat32s = reflect.TypeOf([]float32(nil))
|
||||||
|
sliceOfFloat64s = reflect.TypeOf([]float64(nil))
|
||||||
|
sliceOfDurations = reflect.TypeOf([]time.Duration(nil))
|
||||||
|
)
|
||||||
|
|
||||||
|
// CustomParsers is a friendly name for the type that `ParseWithFuncs()` accepts
|
||||||
|
type CustomParsers map[reflect.Type]ParserFunc
|
||||||
|
|
||||||
|
// ParserFunc defines the signature of a function that can be used within `CustomParsers`
|
||||||
|
type ParserFunc func(v string) (interface{}, error)
|
||||||
|
|
||||||
|
// Parse parses a struct containing `env` tags and loads its values from
|
||||||
|
// environment variables.
|
||||||
|
func Parse(v interface{}) error {
|
||||||
|
ptrRef := reflect.ValueOf(v)
|
||||||
|
if ptrRef.Kind() != reflect.Ptr {
|
||||||
|
return ErrNotAStructPtr
|
||||||
|
}
|
||||||
|
ref := ptrRef.Elem()
|
||||||
|
if ref.Kind() != reflect.Struct {
|
||||||
|
return ErrNotAStructPtr
|
||||||
|
}
|
||||||
|
return doParse(ref, make(map[reflect.Type]ParserFunc, 0))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseWithFuncs is the same as `Parse` except it also allows the user to pass
|
||||||
|
// in custom parsers.
|
||||||
|
func ParseWithFuncs(v interface{}, funcMap CustomParsers) error {
|
||||||
|
ptrRef := reflect.ValueOf(v)
|
||||||
|
if ptrRef.Kind() != reflect.Ptr {
|
||||||
|
return ErrNotAStructPtr
|
||||||
|
}
|
||||||
|
ref := ptrRef.Elem()
|
||||||
|
if ref.Kind() != reflect.Struct {
|
||||||
|
return ErrNotAStructPtr
|
||||||
|
}
|
||||||
|
return doParse(ref, funcMap)
|
||||||
|
}
|
||||||
|
|
||||||
|
func doParse(ref reflect.Value, funcMap CustomParsers) error {
|
||||||
|
refType := ref.Type()
|
||||||
|
var errorList []string
|
||||||
|
|
||||||
|
for i := 0; i < refType.NumField(); i++ {
|
||||||
|
refField := ref.Field(i)
|
||||||
|
if reflect.Ptr == refField.Kind() && !refField.IsNil() && refField.CanSet() {
|
||||||
|
err := Parse(refField.Interface())
|
||||||
|
if nil != err {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
refTypeField := refType.Field(i)
|
||||||
|
value, err := get(refTypeField)
|
||||||
|
if err != nil {
|
||||||
|
errorList = append(errorList, err.Error())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if value == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err := set(refField, refTypeField, value, funcMap); err != nil {
|
||||||
|
errorList = append(errorList, err.Error())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if OnEnvVarSet != nil {
|
||||||
|
OnEnvVarSet(refTypeField, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(errorList) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return errors.New(strings.Join(errorList, ". "))
|
||||||
|
}
|
||||||
|
|
||||||
|
func get(field reflect.StructField) (string, error) {
|
||||||
|
var (
|
||||||
|
val string
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
key, opts := parseKeyForOption(field.Tag.Get("env"))
|
||||||
|
|
||||||
|
defaultValue := field.Tag.Get("envDefault")
|
||||||
|
val = getOr(key, defaultValue)
|
||||||
|
|
||||||
|
if len(opts) > 0 {
|
||||||
|
for _, opt := range opts {
|
||||||
|
// The only option supported is "required".
|
||||||
|
switch opt {
|
||||||
|
case "":
|
||||||
|
break
|
||||||
|
case "required":
|
||||||
|
val, err = getRequired(key)
|
||||||
|
default:
|
||||||
|
err = errors.New("Env tag option " + opt + " not supported.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return val, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// split the env tag's key into the expected key and desired option, if any.
|
||||||
|
func parseKeyForOption(key string) (string, []string) {
|
||||||
|
opts := strings.Split(key, ",")
|
||||||
|
return opts[0], opts[1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
func getRequired(key string) (string, error) {
|
||||||
|
if value, ok := os.LookupEnv(key); ok {
|
||||||
|
return value, nil
|
||||||
|
}
|
||||||
|
// We do not use fmt.Errorf to avoid another import.
|
||||||
|
return "", errors.New("Required environment variable " + key + " is not set")
|
||||||
|
}
|
||||||
|
|
||||||
|
func getOr(key, defaultValue string) string {
|
||||||
|
value, ok := os.LookupEnv(key)
|
||||||
|
if ok {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
return defaultValue
|
||||||
|
}
|
||||||
|
|
||||||
|
func set(field reflect.Value, refType reflect.StructField, value string, funcMap CustomParsers) error {
|
||||||
|
switch field.Kind() {
|
||||||
|
case reflect.Slice:
|
||||||
|
separator := refType.Tag.Get("envSeparator")
|
||||||
|
return handleSlice(field, value, separator)
|
||||||
|
case reflect.String:
|
||||||
|
field.SetString(value)
|
||||||
|
case reflect.Bool:
|
||||||
|
bvalue, err := strconv.ParseBool(value)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
field.SetBool(bvalue)
|
||||||
|
case reflect.Int:
|
||||||
|
intValue, err := strconv.ParseInt(value, 10, 32)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
field.SetInt(intValue)
|
||||||
|
case reflect.Uint:
|
||||||
|
uintValue, err := strconv.ParseUint(value, 10, 32)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
field.SetUint(uintValue)
|
||||||
|
case reflect.Float32:
|
||||||
|
v, err := strconv.ParseFloat(value, 32)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
field.SetFloat(v)
|
||||||
|
case reflect.Float64:
|
||||||
|
v, err := strconv.ParseFloat(value, 64)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
field.Set(reflect.ValueOf(v))
|
||||||
|
case reflect.Int64:
|
||||||
|
if refType.Type.String() == "time.Duration" {
|
||||||
|
dValue, err := time.ParseDuration(value)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
field.Set(reflect.ValueOf(dValue))
|
||||||
|
} else {
|
||||||
|
intValue, err := strconv.ParseInt(value, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
field.SetInt(intValue)
|
||||||
|
}
|
||||||
|
case reflect.Uint64:
|
||||||
|
uintValue, err := strconv.ParseUint(value, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
field.SetUint(uintValue)
|
||||||
|
case reflect.Struct:
|
||||||
|
return handleStruct(field, refType, value, funcMap)
|
||||||
|
default:
|
||||||
|
parserFunc, ok := funcMap[refType.Type]
|
||||||
|
if !ok {
|
||||||
|
return ErrUnsupportedType
|
||||||
|
}
|
||||||
|
val, err := parserFunc(value)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
field.Set(reflect.ValueOf(val))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleStruct(field reflect.Value, refType reflect.StructField, value string, funcMap CustomParsers) error {
|
||||||
|
// Does the custom parser func map contain this type?
|
||||||
|
parserFunc, ok := funcMap[field.Type()]
|
||||||
|
if !ok {
|
||||||
|
// Map does not contain a custom parser for this type
|
||||||
|
return ErrUnsupportedType
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call on the custom parser func
|
||||||
|
data, err := parserFunc(value)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Custom parser error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the field to the data returned by the customer parser func
|
||||||
|
rv := reflect.ValueOf(data)
|
||||||
|
field.Set(rv)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleSlice(field reflect.Value, value, separator string) error {
|
||||||
|
if separator == "" {
|
||||||
|
separator = ","
|
||||||
|
}
|
||||||
|
|
||||||
|
splitData := strings.Split(value, separator)
|
||||||
|
|
||||||
|
switch field.Type() {
|
||||||
|
case sliceOfStrings:
|
||||||
|
field.Set(reflect.ValueOf(splitData))
|
||||||
|
case sliceOfInts:
|
||||||
|
intData, err := parseInts(splitData)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
field.Set(reflect.ValueOf(intData))
|
||||||
|
case sliceOfInt64s:
|
||||||
|
int64Data, err := parseInt64s(splitData)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
field.Set(reflect.ValueOf(int64Data))
|
||||||
|
case sliceOfUint64s:
|
||||||
|
uint64Data, err := parseUint64s(splitData)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
field.Set(reflect.ValueOf(uint64Data))
|
||||||
|
case sliceOfFloat32s:
|
||||||
|
data, err := parseFloat32s(splitData)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
field.Set(reflect.ValueOf(data))
|
||||||
|
case sliceOfFloat64s:
|
||||||
|
data, err := parseFloat64s(splitData)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
field.Set(reflect.ValueOf(data))
|
||||||
|
case sliceOfBools:
|
||||||
|
boolData, err := parseBools(splitData)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
field.Set(reflect.ValueOf(boolData))
|
||||||
|
case sliceOfDurations:
|
||||||
|
durationData, err := parseDurations(splitData)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
field.Set(reflect.ValueOf(durationData))
|
||||||
|
default:
|
||||||
|
return ErrUnsupportedSliceType
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseInts(data []string) ([]int, error) {
|
||||||
|
intSlice := make([]int, 0, len(data))
|
||||||
|
|
||||||
|
for _, v := range data {
|
||||||
|
intValue, err := strconv.ParseInt(v, 10, 32)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
intSlice = append(intSlice, int(intValue))
|
||||||
|
}
|
||||||
|
return intSlice, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseInt64s(data []string) ([]int64, error) {
|
||||||
|
intSlice := make([]int64, 0, len(data))
|
||||||
|
|
||||||
|
for _, v := range data {
|
||||||
|
intValue, err := strconv.ParseInt(v, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
intSlice = append(intSlice, int64(intValue))
|
||||||
|
}
|
||||||
|
return intSlice, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseUint64s(data []string) ([]uint64, error) {
|
||||||
|
var uintSlice []uint64
|
||||||
|
|
||||||
|
for _, v := range data {
|
||||||
|
uintValue, err := strconv.ParseUint(v, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
uintSlice = append(uintSlice, uint64(uintValue))
|
||||||
|
}
|
||||||
|
return uintSlice, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseFloat32s(data []string) ([]float32, error) {
|
||||||
|
float32Slice := make([]float32, 0, len(data))
|
||||||
|
|
||||||
|
for _, v := range data {
|
||||||
|
data, err := strconv.ParseFloat(v, 32)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
float32Slice = append(float32Slice, float32(data))
|
||||||
|
}
|
||||||
|
return float32Slice, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseFloat64s(data []string) ([]float64, error) {
|
||||||
|
float64Slice := make([]float64, 0, len(data))
|
||||||
|
|
||||||
|
for _, v := range data {
|
||||||
|
data, err := strconv.ParseFloat(v, 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
float64Slice = append(float64Slice, float64(data))
|
||||||
|
}
|
||||||
|
return float64Slice, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseBools(data []string) ([]bool, error) {
|
||||||
|
boolSlice := make([]bool, 0, len(data))
|
||||||
|
|
||||||
|
for _, v := range data {
|
||||||
|
bvalue, err := strconv.ParseBool(v)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
boolSlice = append(boolSlice, bvalue)
|
||||||
|
}
|
||||||
|
return boolSlice, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseDurations(data []string) ([]time.Duration, error) {
|
||||||
|
durationSlice := make([]time.Duration, 0, len(data))
|
||||||
|
|
||||||
|
for _, v := range data {
|
||||||
|
dvalue, err := time.ParseDuration(v)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
durationSlice = append(durationSlice, dvalue)
|
||||||
|
}
|
||||||
|
return durationSlice, nil
|
||||||
|
}
|
550
vendor/github.com/caarlos0/env/env_test.go
generated
vendored
Normal file
550
vendor/github.com/caarlos0/env/env_test.go
generated
vendored
Normal file
@ -0,0 +1,550 @@
|
|||||||
|
package env_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/caarlos0/env"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
Some string `env:"somevar"`
|
||||||
|
Other bool `env:"othervar"`
|
||||||
|
Port int `env:"PORT"`
|
||||||
|
Int64Val int64 `env:"INT64VAL"`
|
||||||
|
UintVal uint `env:"UINTVAL"`
|
||||||
|
Uint64Val uint64 `env:"UINT64VAL"`
|
||||||
|
NotAnEnv string
|
||||||
|
DatabaseURL string `env:"DATABASE_URL" envDefault:"postgres://localhost:5432/db"`
|
||||||
|
Strings []string `env:"STRINGS"`
|
||||||
|
SepStrings []string `env:"SEPSTRINGS" envSeparator:":"`
|
||||||
|
Numbers []int `env:"NUMBERS"`
|
||||||
|
Numbers64 []int64 `env:"NUMBERS64"`
|
||||||
|
UNumbers64 []uint64 `env:"UNUMBERS64"`
|
||||||
|
Bools []bool `env:"BOOLS"`
|
||||||
|
Duration time.Duration `env:"DURATION"`
|
||||||
|
Float32 float32 `env:"FLOAT32"`
|
||||||
|
Float64 float64 `env:"FLOAT64"`
|
||||||
|
Float32s []float32 `env:"FLOAT32S"`
|
||||||
|
Float64s []float64 `env:"FLOAT64S"`
|
||||||
|
Durations []time.Duration `env:"DURATIONS"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ParentStruct struct {
|
||||||
|
InnerStruct *InnerStruct
|
||||||
|
unexported *InnerStruct
|
||||||
|
Ignored *http.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
type InnerStruct struct {
|
||||||
|
Inner string `env:"innervar"`
|
||||||
|
Number uint `env:"innernum"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParsesEnv(t *testing.T) {
|
||||||
|
os.Setenv("somevar", "somevalue")
|
||||||
|
os.Setenv("othervar", "true")
|
||||||
|
os.Setenv("PORT", "8080")
|
||||||
|
os.Setenv("STRINGS", "string1,string2,string3")
|
||||||
|
os.Setenv("SEPSTRINGS", "string1:string2:string3")
|
||||||
|
os.Setenv("NUMBERS", "1,2,3,4")
|
||||||
|
os.Setenv("NUMBERS64", "1,2,2147483640,-2147483640")
|
||||||
|
os.Setenv("UNUMBERS64", "1,2,214748364011,9147483641")
|
||||||
|
os.Setenv("BOOLS", "t,TRUE,0,1")
|
||||||
|
os.Setenv("DURATION", "1s")
|
||||||
|
os.Setenv("FLOAT32", "3.40282346638528859811704183484516925440e+38")
|
||||||
|
os.Setenv("FLOAT64", "1.797693134862315708145274237317043567981e+308")
|
||||||
|
os.Setenv("FLOAT32S", "1.0,2.0,3.0")
|
||||||
|
os.Setenv("FLOAT64S", "1.0,2.0,3.0")
|
||||||
|
os.Setenv("UINTVAL", "44")
|
||||||
|
os.Setenv("UINT64VAL", "6464")
|
||||||
|
os.Setenv("INT64VAL", "-7575")
|
||||||
|
os.Setenv("DURATIONS", "1s,2s,3s")
|
||||||
|
|
||||||
|
defer os.Clearenv()
|
||||||
|
|
||||||
|
cfg := Config{}
|
||||||
|
assert.NoError(t, env.Parse(&cfg))
|
||||||
|
assert.Equal(t, "somevalue", cfg.Some)
|
||||||
|
assert.Equal(t, true, cfg.Other)
|
||||||
|
assert.Equal(t, 8080, cfg.Port)
|
||||||
|
assert.Equal(t, uint(44), cfg.UintVal)
|
||||||
|
assert.Equal(t, int64(-7575), cfg.Int64Val)
|
||||||
|
assert.Equal(t, uint64(6464), cfg.Uint64Val)
|
||||||
|
assert.Equal(t, []string{"string1", "string2", "string3"}, cfg.Strings)
|
||||||
|
assert.Equal(t, []string{"string1", "string2", "string3"}, cfg.SepStrings)
|
||||||
|
assert.Equal(t, []int{1, 2, 3, 4}, cfg.Numbers)
|
||||||
|
assert.Equal(t, []int64{1, 2, 2147483640, -2147483640}, cfg.Numbers64)
|
||||||
|
assert.Equal(t, []uint64{1, 2, 214748364011, 9147483641}, cfg.UNumbers64)
|
||||||
|
assert.Equal(t, []bool{true, true, false, true}, cfg.Bools)
|
||||||
|
d1, _ := time.ParseDuration("1s")
|
||||||
|
assert.Equal(t, d1, cfg.Duration)
|
||||||
|
f32 := float32(3.40282346638528859811704183484516925440e+38)
|
||||||
|
assert.Equal(t, f32, cfg.Float32)
|
||||||
|
f64 := float64(1.797693134862315708145274237317043567981e+308)
|
||||||
|
assert.Equal(t, f64, cfg.Float64)
|
||||||
|
assert.Equal(t, []float32{float32(1.0), float32(2.0), float32(3.0)}, cfg.Float32s)
|
||||||
|
assert.Equal(t, []float64{float64(1.0), float64(2.0), float64(3.0)}, cfg.Float64s)
|
||||||
|
d2, _ := time.ParseDuration("2s")
|
||||||
|
d3, _ := time.ParseDuration("3s")
|
||||||
|
assert.Equal(t, []time.Duration{d1, d2, d3}, cfg.Durations)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParsesEnvInner(t *testing.T) {
|
||||||
|
os.Setenv("innervar", "someinnervalue")
|
||||||
|
defer os.Clearenv()
|
||||||
|
cfg := ParentStruct{
|
||||||
|
InnerStruct: &InnerStruct{},
|
||||||
|
unexported: &InnerStruct{},
|
||||||
|
}
|
||||||
|
assert.NoError(t, env.Parse(&cfg))
|
||||||
|
assert.Equal(t, "someinnervalue", cfg.InnerStruct.Inner)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParsesEnvInnerNil(t *testing.T) {
|
||||||
|
os.Setenv("innervar", "someinnervalue")
|
||||||
|
defer os.Clearenv()
|
||||||
|
cfg := ParentStruct{}
|
||||||
|
assert.NoError(t, env.Parse(&cfg))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParsesEnvInnerInvalid(t *testing.T) {
|
||||||
|
os.Setenv("innernum", "-547")
|
||||||
|
defer os.Clearenv()
|
||||||
|
cfg := ParentStruct{
|
||||||
|
InnerStruct: &InnerStruct{},
|
||||||
|
}
|
||||||
|
assert.Error(t, env.Parse(&cfg))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEmptyVars(t *testing.T) {
|
||||||
|
cfg := Config{}
|
||||||
|
assert.NoError(t, env.Parse(&cfg))
|
||||||
|
assert.Equal(t, "", cfg.Some)
|
||||||
|
assert.Equal(t, false, cfg.Other)
|
||||||
|
assert.Equal(t, 0, cfg.Port)
|
||||||
|
assert.Equal(t, uint(0), cfg.UintVal)
|
||||||
|
assert.Equal(t, uint64(0), cfg.Uint64Val)
|
||||||
|
assert.Equal(t, int64(0), cfg.Int64Val)
|
||||||
|
assert.Equal(t, 0, len(cfg.Strings))
|
||||||
|
assert.Equal(t, 0, len(cfg.SepStrings))
|
||||||
|
assert.Equal(t, 0, len(cfg.Numbers))
|
||||||
|
assert.Equal(t, 0, len(cfg.Bools))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPassAnInvalidPtr(t *testing.T) {
|
||||||
|
var thisShouldBreak int
|
||||||
|
assert.Error(t, env.Parse(&thisShouldBreak))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPassReference(t *testing.T) {
|
||||||
|
cfg := Config{}
|
||||||
|
assert.Error(t, env.Parse(cfg))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInvalidBool(t *testing.T) {
|
||||||
|
os.Setenv("othervar", "should-be-a-bool")
|
||||||
|
defer os.Clearenv()
|
||||||
|
|
||||||
|
cfg := Config{}
|
||||||
|
assert.Error(t, env.Parse(&cfg))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInvalidInt(t *testing.T) {
|
||||||
|
os.Setenv("PORT", "should-be-an-int")
|
||||||
|
defer os.Clearenv()
|
||||||
|
|
||||||
|
cfg := Config{}
|
||||||
|
assert.Error(t, env.Parse(&cfg))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInvalidUint(t *testing.T) {
|
||||||
|
os.Setenv("UINTVAL", "-44")
|
||||||
|
defer os.Clearenv()
|
||||||
|
|
||||||
|
cfg := Config{}
|
||||||
|
assert.Error(t, env.Parse(&cfg))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInvalidFloat32(t *testing.T) {
|
||||||
|
os.Setenv("FLOAT32", "AAA")
|
||||||
|
defer os.Clearenv()
|
||||||
|
|
||||||
|
cfg := Config{}
|
||||||
|
assert.Error(t, env.Parse(&cfg))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInvalidFloat64(t *testing.T) {
|
||||||
|
os.Setenv("FLOAT64", "AAA")
|
||||||
|
defer os.Clearenv()
|
||||||
|
|
||||||
|
cfg := Config{}
|
||||||
|
assert.Error(t, env.Parse(&cfg))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInvalidUint64(t *testing.T) {
|
||||||
|
os.Setenv("UINT64VAL", "AAA")
|
||||||
|
defer os.Clearenv()
|
||||||
|
|
||||||
|
cfg := Config{}
|
||||||
|
assert.Error(t, env.Parse(&cfg))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInvalidInt64(t *testing.T) {
|
||||||
|
os.Setenv("INT64VAL", "AAA")
|
||||||
|
defer os.Clearenv()
|
||||||
|
|
||||||
|
cfg := Config{}
|
||||||
|
assert.Error(t, env.Parse(&cfg))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInvalidInt64Slice(t *testing.T) {
|
||||||
|
type config struct {
|
||||||
|
BadFloats []int64 `env:"BADINTS"`
|
||||||
|
}
|
||||||
|
|
||||||
|
os.Setenv("BADINTS", "A,2,3")
|
||||||
|
cfg := &config{}
|
||||||
|
assert.Error(t, env.Parse(cfg))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInvalidUInt64Slice(t *testing.T) {
|
||||||
|
type config struct {
|
||||||
|
BadFloats []uint64 `env:"BADINTS"`
|
||||||
|
}
|
||||||
|
|
||||||
|
os.Setenv("BADFLOATS", "A,2,3")
|
||||||
|
cfg := &config{}
|
||||||
|
assert.Error(t, env.Parse(cfg))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInvalidFloat32Slice(t *testing.T) {
|
||||||
|
type config struct {
|
||||||
|
BadFloats []float32 `env:"BADFLOATS"`
|
||||||
|
}
|
||||||
|
|
||||||
|
os.Setenv("BADFLOATS", "A,2.0,3.0")
|
||||||
|
cfg := &config{}
|
||||||
|
assert.Error(t, env.Parse(cfg))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInvalidFloat64Slice(t *testing.T) {
|
||||||
|
type config struct {
|
||||||
|
BadFloats []float64 `env:"BADFLOATS"`
|
||||||
|
}
|
||||||
|
|
||||||
|
os.Setenv("BADFLOATS", "A,2.0,3.0")
|
||||||
|
cfg := &config{}
|
||||||
|
assert.Error(t, env.Parse(cfg))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInvalidBoolsSlice(t *testing.T) {
|
||||||
|
type config struct {
|
||||||
|
BadBools []bool `env:"BADBOOLS"`
|
||||||
|
}
|
||||||
|
|
||||||
|
os.Setenv("BADBOOLS", "t,f,TRUE,faaaalse")
|
||||||
|
cfg := &config{}
|
||||||
|
assert.Error(t, env.Parse(cfg))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInvalidDuration(t *testing.T) {
|
||||||
|
os.Setenv("DURATION", "should-be-a-valid-duration")
|
||||||
|
defer os.Clearenv()
|
||||||
|
|
||||||
|
cfg := Config{}
|
||||||
|
assert.Error(t, env.Parse(&cfg))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInvalidDurations(t *testing.T) {
|
||||||
|
os.Setenv("DURATIONS", "1s,contains-an-invalid-duration,3s")
|
||||||
|
defer os.Clearenv()
|
||||||
|
|
||||||
|
cfg := Config{}
|
||||||
|
assert.Error(t, env.Parse(&cfg))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParsesDefaultConfig(t *testing.T) {
|
||||||
|
cfg := Config{}
|
||||||
|
assert.NoError(t, env.Parse(&cfg))
|
||||||
|
assert.Equal(t, "postgres://localhost:5432/db", cfg.DatabaseURL)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseStructWithoutEnvTag(t *testing.T) {
|
||||||
|
cfg := Config{}
|
||||||
|
assert.NoError(t, env.Parse(&cfg))
|
||||||
|
assert.Empty(t, cfg.NotAnEnv)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseStructWithInvalidFieldKind(t *testing.T) {
|
||||||
|
type config struct {
|
||||||
|
WontWorkByte byte `env:"BLAH"`
|
||||||
|
}
|
||||||
|
os.Setenv("BLAH", "a")
|
||||||
|
cfg := config{}
|
||||||
|
assert.Error(t, env.Parse(&cfg))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnsupportedSliceType(t *testing.T) {
|
||||||
|
type config struct {
|
||||||
|
WontWork []map[int]int `env:"WONTWORK"`
|
||||||
|
}
|
||||||
|
|
||||||
|
os.Setenv("WONTWORK", "1,2,3")
|
||||||
|
defer os.Clearenv()
|
||||||
|
|
||||||
|
cfg := &config{}
|
||||||
|
assert.Error(t, env.Parse(cfg))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBadSeparator(t *testing.T) {
|
||||||
|
type config struct {
|
||||||
|
WontWork []int `env:"WONTWORK" envSeparator:":"`
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg := &config{}
|
||||||
|
os.Setenv("WONTWORK", "1,2,3,4")
|
||||||
|
defer os.Clearenv()
|
||||||
|
|
||||||
|
assert.Error(t, env.Parse(cfg))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNoErrorRequiredSet(t *testing.T) {
|
||||||
|
type config struct {
|
||||||
|
IsRequired string `env:"IS_REQUIRED,required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg := &config{}
|
||||||
|
|
||||||
|
os.Setenv("IS_REQUIRED", "val")
|
||||||
|
defer os.Clearenv()
|
||||||
|
assert.NoError(t, env.Parse(cfg))
|
||||||
|
assert.Equal(t, "val", cfg.IsRequired)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestErrorRequiredNotSet(t *testing.T) {
|
||||||
|
type config struct {
|
||||||
|
IsRequired string `env:"IS_REQUIRED,required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg := &config{}
|
||||||
|
assert.Error(t, env.Parse(cfg))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCustomParser(t *testing.T) {
|
||||||
|
type foo struct {
|
||||||
|
name string
|
||||||
|
}
|
||||||
|
|
||||||
|
type config struct {
|
||||||
|
Var foo `env:"VAR"`
|
||||||
|
}
|
||||||
|
|
||||||
|
os.Setenv("VAR", "test")
|
||||||
|
|
||||||
|
customParserFunc := func(v string) (interface{}, error) {
|
||||||
|
return foo{name: v}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg := &config{}
|
||||||
|
err := env.ParseWithFuncs(cfg, map[reflect.Type]env.ParserFunc{
|
||||||
|
reflect.TypeOf(foo{}): customParserFunc,
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, cfg.Var.name, "test")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseWithFuncsNoPtr(t *testing.T) {
|
||||||
|
type foo struct{}
|
||||||
|
err := env.ParseWithFuncs(foo{}, nil)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Equal(t, err, env.ErrNotAStructPtr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseWithFuncsInvalidType(t *testing.T) {
|
||||||
|
var c int
|
||||||
|
err := env.ParseWithFuncs(&c, nil)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Equal(t, err, env.ErrNotAStructPtr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCustomParserError(t *testing.T) {
|
||||||
|
type foo struct {
|
||||||
|
name string
|
||||||
|
}
|
||||||
|
|
||||||
|
type config struct {
|
||||||
|
Var foo `env:"VAR"`
|
||||||
|
}
|
||||||
|
|
||||||
|
os.Setenv("VAR", "test")
|
||||||
|
|
||||||
|
customParserFunc := func(v string) (interface{}, error) {
|
||||||
|
return nil, errors.New("something broke")
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg := &config{}
|
||||||
|
err := env.ParseWithFuncs(cfg, map[reflect.Type]env.ParserFunc{
|
||||||
|
reflect.TypeOf(foo{}): customParserFunc,
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.Empty(t, cfg.Var.name, "Var.name should not be filled out when parse errors")
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Equal(t, err.Error(), "Custom parser error: something broke")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCustomParserBasicType(t *testing.T) {
|
||||||
|
type ConstT int32
|
||||||
|
|
||||||
|
type config struct {
|
||||||
|
Const ConstT `env:"CONST_VAL"`
|
||||||
|
}
|
||||||
|
|
||||||
|
exp := ConstT(123)
|
||||||
|
os.Setenv("CONST_VAL", fmt.Sprintf("%d", exp))
|
||||||
|
|
||||||
|
customParserFunc := func(v string) (interface{}, error) {
|
||||||
|
i, err := strconv.Atoi(v)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
r := ConstT(i)
|
||||||
|
return r, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg := &config{}
|
||||||
|
err := env.ParseWithFuncs(cfg, map[reflect.Type]env.ParserFunc{
|
||||||
|
reflect.TypeOf(ConstT(0)): customParserFunc,
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, exp, cfg.Const)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TypeCustomParserBasicInvalid(t *testing.T) {
|
||||||
|
type ConstT int32
|
||||||
|
|
||||||
|
type config struct {
|
||||||
|
Const ConstT `env:"CONST_VAL"`
|
||||||
|
}
|
||||||
|
|
||||||
|
os.Setenv("CONST_VAL", "foobar")
|
||||||
|
|
||||||
|
expErr := errors.New("Random error")
|
||||||
|
customParserFunc := func(_ string) (interface{}, error) {
|
||||||
|
return nil, expErr
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg := &config{}
|
||||||
|
err := env.ParseWithFuncs(cfg, map[reflect.Type]env.ParserFunc{
|
||||||
|
reflect.TypeOf(ConstT(0)): customParserFunc,
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.Empty(t, cfg.Const)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Equal(t, expErr, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCustomParserBasicUnsupported(t *testing.T) {
|
||||||
|
type ConstT int32
|
||||||
|
|
||||||
|
type config struct {
|
||||||
|
Const ConstT `env:"CONST_VAL"`
|
||||||
|
}
|
||||||
|
|
||||||
|
exp := ConstT(123)
|
||||||
|
os.Setenv("CONST_VAL", fmt.Sprintf("%d", exp))
|
||||||
|
|
||||||
|
cfg := &config{}
|
||||||
|
err := env.Parse(cfg)
|
||||||
|
|
||||||
|
assert.Zero(t, cfg.Const)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Equal(t, env.ErrUnsupportedType, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnsupportedStructType(t *testing.T) {
|
||||||
|
type config struct {
|
||||||
|
Foo http.Client `env:"FOO"`
|
||||||
|
}
|
||||||
|
|
||||||
|
os.Setenv("FOO", "foo")
|
||||||
|
|
||||||
|
cfg := &config{}
|
||||||
|
err := env.Parse(cfg)
|
||||||
|
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Equal(t, env.ErrUnsupportedType, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEmptyOption(t *testing.T) {
|
||||||
|
type config struct {
|
||||||
|
Var string `env:"VAR,"`
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg := &config{}
|
||||||
|
|
||||||
|
os.Setenv("VAR", "val")
|
||||||
|
defer os.Clearenv()
|
||||||
|
assert.NoError(t, env.Parse(cfg))
|
||||||
|
assert.Equal(t, "val", cfg.Var)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestErrorOptionNotRecognized(t *testing.T) {
|
||||||
|
type config struct {
|
||||||
|
Var string `env:"VAR,not_supported!"`
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg := &config{}
|
||||||
|
assert.Error(t, env.Parse(cfg))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleParse() {
|
||||||
|
type config struct {
|
||||||
|
Home string `env:"HOME"`
|
||||||
|
Port int `env:"PORT" envDefault:"3000"`
|
||||||
|
IsProduction bool `env:"PRODUCTION"`
|
||||||
|
}
|
||||||
|
os.Setenv("HOME", "/tmp/fakehome")
|
||||||
|
cfg := config{}
|
||||||
|
env.Parse(&cfg)
|
||||||
|
fmt.Println(cfg)
|
||||||
|
// Output: {/tmp/fakehome 3000 false}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleParseRequiredField() {
|
||||||
|
type config struct {
|
||||||
|
Home string `env:"HOME"`
|
||||||
|
Port int `env:"PORT" envDefault:"3000"`
|
||||||
|
IsProduction bool `env:"PRODUCTION"`
|
||||||
|
SecretKey string `env:"SECRET_KEY,required"`
|
||||||
|
}
|
||||||
|
os.Setenv("HOME", "/tmp/fakehome")
|
||||||
|
cfg := config{}
|
||||||
|
err := env.Parse(&cfg)
|
||||||
|
fmt.Println(err)
|
||||||
|
// Output: Required environment variable SECRET_KEY is not set
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleParseMultipleOptions() {
|
||||||
|
type config struct {
|
||||||
|
Home string `env:"HOME"`
|
||||||
|
Port int `env:"PORT" envDefault:"3000"`
|
||||||
|
IsProduction bool `env:"PRODUCTION"`
|
||||||
|
SecretKey string `env:"SECRET_KEY,required,option1"`
|
||||||
|
}
|
||||||
|
os.Setenv("HOME", "/tmp/fakehome")
|
||||||
|
cfg := config{}
|
||||||
|
err := env.Parse(&cfg)
|
||||||
|
fmt.Println(err)
|
||||||
|
// Output: Env tag option option1 not supported.
|
||||||
|
}
|
48
vendor/github.com/caarlos0/env/examples/first.go
generated
vendored
Normal file
48
vendor/github.com/caarlos0/env/examples/first.go
generated
vendored
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/caarlos0/env"
|
||||||
|
)
|
||||||
|
|
||||||
|
type config struct {
|
||||||
|
Home string `env:"HOME"`
|
||||||
|
Port int `env:"PORT" envDefault:"3000"`
|
||||||
|
IsProduction bool `env:"PRODUCTION"`
|
||||||
|
Hosts []string `env:"HOSTS" envSeparator:":"`
|
||||||
|
Duration time.Duration `env:"DURATION"`
|
||||||
|
ExampleFoo Foo `env:"EXAMPLE_FOO"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Foo struct {
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
cfg := config{}
|
||||||
|
|
||||||
|
// Parse for built-in types
|
||||||
|
if err := env.Parse(&cfg); err != nil {
|
||||||
|
log.Fatal("Unable to parse envs: ", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// OR w/ a custom parser for `Foo`
|
||||||
|
//
|
||||||
|
// if err := env.ParseWithFuncs(&cfg, env.CustomParsers{
|
||||||
|
// reflect.TypeOf(Foo{}): fooParser,
|
||||||
|
// }); err != nil {
|
||||||
|
// log.Fatal("Unable to parse envs: ", err)
|
||||||
|
// }
|
||||||
|
|
||||||
|
fmt.Printf("%+v\n", cfg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func fooParser(value string) (interface{}, error) {
|
||||||
|
return Foo{
|
||||||
|
Name: strings.ToUpper(value),
|
||||||
|
}, nil
|
||||||
|
}
|
35
vendor/github.com/caarlos0/env/parsers/README.md
generated
vendored
Normal file
35
vendor/github.com/caarlos0/env/parsers/README.md
generated
vendored
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
parsers
|
||||||
|
=======
|
||||||
|
This directory contains pre-built, custom parsers that can be used with `env.ParseWithFuncs`
|
||||||
|
to facilitate the parsing of envs that are not basic types.
|
||||||
|
|
||||||
|
Example Usage:
|
||||||
|
|
||||||
|
```golang
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net/url"
|
||||||
|
|
||||||
|
"github.com/caarlos0/env"
|
||||||
|
"github.com/caarlos0/env/parsers"
|
||||||
|
)
|
||||||
|
|
||||||
|
type config struct {
|
||||||
|
ExampleURL url.URL `env:"EXAMPLE_URL" envDefault:"https://google.com"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
cfg := config{}
|
||||||
|
|
||||||
|
if err := env.ParseWithFuncs(&cfg, env.CustomParsers{
|
||||||
|
parsers.URLType: parsers.URLFunc,
|
||||||
|
}); err != nil {
|
||||||
|
log.Fatal("Unable to parse envs: ", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("Scheme: %v Host: %v\n", cfg.ExampleURL.Scheme, cfg.ExampleURL.Host)
|
||||||
|
}
|
||||||
|
```
|
23
vendor/github.com/caarlos0/env/parsers/parsers.go
generated
vendored
Normal file
23
vendor/github.com/caarlos0/env/parsers/parsers.go
generated
vendored
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
// Package parsers contains custom parser funcs for common, non-built-in types
|
||||||
|
package parsers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// URLType is a helper var that represents the `reflect.Type`` of `url.URL`
|
||||||
|
URLType = reflect.TypeOf(url.URL{})
|
||||||
|
)
|
||||||
|
|
||||||
|
// URLFunc is a basic parser for the url.URL type that should be used with `env.ParseWithFuncs()`
|
||||||
|
func URLFunc(v string) (interface{}, error) {
|
||||||
|
u, err := url.Parse(v)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Unable to complete URL parse: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return *u, nil
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user