⬆️ Upgraded caarlos0/env package

This commit is contained in:
Maxim Lebedev 2023-03-15 06:16:43 +06:00
parent 1ee4956afd
commit dd29a48db3
Signed by: toby3d
GPG Key ID: 1F14E25B7C119FC5
14 changed files with 384 additions and 85 deletions

2
go.mod
View File

@ -4,7 +4,7 @@ go 1.20
require ( require (
github.com/DATA-DOG/go-sqlmock v1.5.0 github.com/DATA-DOG/go-sqlmock v1.5.0
github.com/caarlos0/env/v6 v6.10.1 github.com/caarlos0/env/v7 v7.1.0
github.com/go-logfmt/logfmt v0.6.0 github.com/go-logfmt/logfmt v0.6.0
github.com/jmoiron/sqlx v1.3.5 github.com/jmoiron/sqlx v1.3.5
github.com/valyala/quicktemplate v1.7.0 github.com/valyala/quicktemplate v1.7.0

4
go.sum
View File

@ -2,8 +2,8 @@ github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20O
github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
github.com/andybalholm/brotli v1.0.2/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= github.com/andybalholm/brotli v1.0.2/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
github.com/andybalholm/brotli v1.0.3/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/andybalholm/brotli v1.0.3/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/caarlos0/env/v6 v6.10.1 h1:t1mPSxNpei6M5yAeu1qtRdPAK29Nbcf/n3G7x+b3/II= github.com/caarlos0/env/v7 v7.1.0 h1:9lzTF5amyQeWHZzuZeKlCb5FWSUxpG1js43mhbY8ozg=
github.com/caarlos0/env/v6 v6.10.1/go.mod h1:hvp/ryKXKipEkcuYjs9mI4bBCg+UI0Yhgm5Zu0ddvwc= github.com/caarlos0/env/v7 v7.1.0/go.mod h1:LPPWniDUq4JaO6Q41vtlyikhMknqymCLBw0eX4dcH1E=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4= github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4=

View File

@ -14,7 +14,7 @@ import (
"path/filepath" "path/filepath"
"time" "time"
"github.com/caarlos0/env/v6" "github.com/caarlos0/env/v7"
"golang.org/x/text/feature/plural" "golang.org/x/text/feature/plural"
"golang.org/x/text/language" "golang.org/x/text/language"
"golang.org/x/text/message" "golang.org/x/text/message"

View File

@ -1,8 +1,8 @@
# env # env
[![Build Status](https://img.shields.io/github/workflow/status/caarlos0/env/build?style=for-the-badge)](https://github.com/caarlos0/env/actions?workflow=build) [![Build Status](https://img.shields.io/github/actions/workflow/status/caarlos0/env/build.yml?branch=main&style=for-the-badge)](https://github.com/caarlos0/env/actions?workflow=build)
[![Coverage Status](https://img.shields.io/codecov/c/gh/caarlos0/env.svg?logo=codecov&style=for-the-badge)](https://codecov.io/gh/caarlos0/env) [![Coverage Status](https://img.shields.io/codecov/c/gh/caarlos0/env.svg?logo=codecov&style=for-the-badge)](https://codecov.io/gh/caarlos0/env)
[![](http://img.shields.io/badge/godoc-reference-5272B4.svg?style=for-the-badge)](https://pkg.go.dev/github.com/caarlos0/env/v6) [![](http://img.shields.io/badge/godoc-reference-5272B4.svg?style=for-the-badge)](https://pkg.go.dev/github.com/caarlos0/env/v7)
A simple and zero-dependencies library to parse environment variables into structs. A simple and zero-dependencies library to parse environment variables into structs.
@ -11,7 +11,7 @@ A simple and zero-dependencies library to parse environment variables into struc
Get the module with: Get the module with:
```sh ```sh
go get github.com/caarlos0/env/v6 go get github.com/caarlos0/env/v7
``` ```
The usage looks like this: The usage looks like this:
@ -23,7 +23,7 @@ import (
"fmt" "fmt"
"time" "time"
"github.com/caarlos0/env/v6" "github.com/caarlos0/env/v7"
) )
type config struct { type config struct {
@ -53,7 +53,14 @@ $ PRODUCTION=true HOSTS="host1:host2:host3" DURATION=1s go run main.go
{Home:/your/home Port:3000 IsProduction:true Hosts:[host1 host2 host3] Duration:1s} {Home:/your/home Port:3000 IsProduction:true Hosts:[host1 host2 host3] Duration:1s}
``` ```
⚠️⚠️⚠️ **Attention:** _unexported fields_ will be **ignored**. ## Caveats
> **Warning**
>
> **This is important!**
- _Unexported fields_ are **ignored**
## Supported types and defaults ## Supported types and defaults
@ -80,11 +87,15 @@ Complete list:
- `encoding.TextUnmarshaler` - `encoding.TextUnmarshaler`
- `url.URL` - `url.URL`
Pointers, slices and slices of pointers of those types are also supported. Pointers, slices and slices of pointers, and maps of those types are also
supported.
You can also use/define a [custom parser func](#custom-parser-funcs) for any You can also use/define a [custom parser func](#custom-parser-funcs) for any
other type you want. other type you want.
You can also use custom keys and values in your maps, as long as you provide a
parser function for them.
If you set the `envDefault` tag for something, this value will be used in the If you set the `envDefault` tag for something, this value will be used in the
case of absence of it in the environment. case of absence of it in the environment.
@ -107,7 +118,7 @@ also accepts a `map[reflect.Type]env.ParserFunc`.
If you add a custom parser for, say `Foo`, it will also be used to parse If you add a custom parser for, say `Foo`, it will also be used to parse
`*Foo` and `[]Foo` types. `*Foo` and `[]Foo` types.
Check the examples in the [go doc](http://pkg.go.dev/github.com/caarlos0/env/v6) Check the examples in the [go doc](http://pkg.go.dev/github.com/caarlos0/env/v7)
for more info. for more info.
### A note about `TextUnmarshaler` and `time.Time` ### A note about `TextUnmarshaler` and `time.Time`
@ -148,7 +159,7 @@ type config struct {
## Not Empty fields ## Not Empty fields
While `required` demands the environment variable to be check, it doesn't check its value. While `required` demands the environment variable to be set, it doesn't check its value.
If you want to make sure the environment is set and not empty, you need to use the `notEmpty` tag option instead (`env:"SOME_ENV,notEmpty"`). If you want to make sure the environment is set and not empty, you need to use the `notEmpty` tag option instead (`env:"SOME_ENV,notEmpty"`).
Example: Example:
@ -185,7 +196,7 @@ package main
import ( import (
"fmt" "fmt"
"time" "time"
"github.com/caarlos0/env/v6" "github.com/caarlos0/env/v7"
) )
type config struct { type config struct {
@ -217,6 +228,46 @@ $ SECRET=/tmp/secret \
## Options ## Options
### Use field names as environment variables by default
If you don't want to set the `env` tag on every field, you can use the
`UseFieldNameByDefault` option.
It will use the field name as environment variable name.
Here's an example:
```go
package main
import (
"fmt"
"log"
"github.com/caarlos0/env/v7"
)
type Config struct {
Username string // will use $USERNAME
Password string // will use $PASSWORD
UserFullName string // will use $USER_FULL_NAME
}
func main() {
cfg := &Config{}
opts := &env.Options{UseFieldNameByDefault: true}
// Load env vars.
if err := env.Parse(cfg, opts); err != nil {
log.Fatal(err)
}
// Print the loaded data.
fmt.Printf("%+v\n", cfg)
}
```
### Environment ### Environment
By setting the `Options.Environment` map you can tell `Parse` to add those `keys` and `values` By setting the `Options.Environment` map you can tell `Parse` to add those `keys` and `values`
@ -232,7 +283,7 @@ import (
"fmt" "fmt"
"log" "log"
"github.com/caarlos0/env/v6" "github.com/caarlos0/env/v7"
) )
type Config struct { type Config struct {
@ -251,7 +302,7 @@ func main() {
} }
// Print the loaded data. // Print the loaded data.
fmt.Printf("%+v\n", cfg.envData) fmt.Printf("%+v\n", cfg)
} }
``` ```
@ -268,7 +319,7 @@ import (
"fmt" "fmt"
"log" "log"
"github.com/caarlos0/env/v6" "github.com/caarlos0/env/v7"
) )
type Config struct { type Config struct {
@ -285,7 +336,7 @@ func main() {
} }
// Print the loaded data. // Print the loaded data.
fmt.Printf("%+v\n", cfg.envData) fmt.Printf("%+v\n", cfg)
} }
``` ```
@ -302,7 +353,7 @@ import (
"fmt" "fmt"
"log" "log"
"github.com/caarlos0/env/v6" "github.com/caarlos0/env/v7"
) )
type Config struct { type Config struct {
@ -318,7 +369,7 @@ type ComplexConfig struct {
func main() { func main() {
cfg := ComplexConfig{} cfg := ComplexConfig{}
if err := Parse(&cfg, Options{ if err := Parse(&cfg, Options{
Prefix: "T_", Prefix: "T_",
Environment: map[string]string{ Environment: map[string]string{
"T_FOO_HOME": "/foo", "T_FOO_HOME": "/foo",
@ -336,7 +387,7 @@ func main() {
} }
// Print the loaded data. // Print the loaded data.
fmt.Printf("%+v\n", cfg.envData) fmt.Printf("%+v\n", cfg)
} }
``` ```
@ -352,7 +403,7 @@ import (
"fmt" "fmt"
"log" "log"
"github.com/caarlos0/env/v6" "github.com/caarlos0/env/v7"
) )
type Config struct { type Config struct {
@ -374,7 +425,7 @@ func main() {
} }
// Print the loaded data. // Print the loaded data.
fmt.Printf("%+v\n", cfg.envData) fmt.Printf("%+v\n", cfg)
} }
``` ```
@ -391,7 +442,7 @@ import (
"fmt" "fmt"
"log" "log"
"github.com/caarlos0/env/v6" "github.com/caarlos0/env/v7"
) )
type Config struct { type Config struct {
@ -409,7 +460,7 @@ func main() {
} }
// Print the loaded data. // Print the loaded data.
fmt.Printf("%+v\n", cfg.envData) fmt.Printf("%+v\n", cfg)
} }
``` ```
@ -425,7 +476,7 @@ import (
"fmt" "fmt"
"log" "log"
"github.com/caarlos0/env/v6" "github.com/caarlos0/env/v7"
) )
type Config struct { type Config struct {
@ -447,6 +498,54 @@ func main() {
} }
``` ```
## Error handling
You can handle the errors the library throws like so:
```go
package main
import (
"fmt"
"log"
"github.com/caarlos0/env/v7"
)
type Config struct {
Username string `env:"USERNAME" envDefault:"admin"`
Password string `env:"PASSWORD"`
}
func main() {
var cfg Config
err := env.Parse(&cfg)
if e, ok := err.(*env.AggregateError); ok {
for _, er := range e.Errors {
switch v := er.(type) {
case env.ParseError:
// handle it
case env.NotStructPtrError:
// handle it
case env.NoParserError:
// handle it
case env.NoSupportedTagOptionError:
// handle it
default:
fmt.Printf("Unknown error type %v", v)
}
}
}
fmt.Printf("%+v", cfg) // {Username:admin Password:123456}
}
```
> **Info**
>
> If you want to check if an specific error is in the chain, you can also use
> `errors.Is()`.
## Stargazers over time ## Stargazers over time
[![Stargazers over time](https://starchart.cc/caarlos0/env.svg)](https://starchart.cc/caarlos0/env) [![Stargazers over time](https://starchart.cc/caarlos0/env.svg)](https://starchart.cc/caarlos0/env)

View File

@ -2,7 +2,6 @@ package env
import ( import (
"encoding" "encoding"
"errors"
"fmt" "fmt"
"net/url" "net/url"
"os" "os"
@ -10,14 +9,11 @@ import (
"strconv" "strconv"
"strings" "strings"
"time" "time"
"unicode"
) )
// nolint: gochecknoglobals // nolint: gochecknoglobals
var ( var (
// ErrNotAStructPtr is returned if you pass something that is not a pointer to a
// Struct to Parse.
ErrNotAStructPtr = errors.New("env: expected a pointer to a Struct")
defaultBuiltInParsers = map[reflect.Kind]ParserFunc{ defaultBuiltInParsers = map[reflect.Kind]ParserFunc{
reflect.Bool: func(v string) (interface{}, error) { reflect.Bool: func(v string) (interface{}, error) {
return strconv.ParseBool(v) return strconv.ParseBool(v)
@ -79,14 +75,14 @@ func defaultTypeParsers() map[reflect.Type]ParserFunc {
reflect.TypeOf(url.URL{}): func(v string) (interface{}, error) { reflect.TypeOf(url.URL{}): func(v string) (interface{}, error) {
u, err := url.Parse(v) u, err := url.Parse(v)
if err != nil { if err != nil {
return nil, fmt.Errorf("unable to parse URL: %v", err) return nil, newParseValueError("unable to parse URL", err)
} }
return *u, nil return *u, nil
}, },
reflect.TypeOf(time.Nanosecond): func(v string) (interface{}, error) { reflect.TypeOf(time.Nanosecond): func(v string) (interface{}, error) {
s, err := time.ParseDuration(v) s, err := time.ParseDuration(v)
if err != nil { if err != nil {
return nil, fmt.Errorf("unable to parse duration: %v", err) return nil, newParseValueError("unable to parse duration", err)
} }
return s, err return s, err
}, },
@ -107,15 +103,20 @@ type Options struct {
// TagName specifies another tagname to use rather than the default env. // TagName specifies another tagname to use rather than the default env.
TagName string TagName string
// RequiredIfNoDef automatically sets all env as required if they do not declare 'envDefault' // RequiredIfNoDef automatically sets all env as required if they do not
// declare 'envDefault'.
RequiredIfNoDef bool RequiredIfNoDef bool
// OnSet allows to run a function when a value is set // OnSet allows to run a function when a value is set.
OnSet OnSetFn OnSet OnSetFn
// Prefix define a prefix for each key // Prefix define a prefix for each key.
Prefix string Prefix string
// UseFieldNameByDefault defines whether or not env should use the field
// name by default if the `env` key is missing.
UseFieldNameByDefault bool
// Sets to true if we have already configured once. // Sets to true if we have already configured once.
configured bool configured bool
} }
@ -150,6 +151,7 @@ func configure(opts []Options) []Options {
if item.Prefix != "" { if item.Prefix != "" {
opt.Prefix = item.Prefix opt.Prefix = item.Prefix
} }
opt.UseFieldNameByDefault = item.UseFieldNameByDefault
opt.RequiredIfNoDef = item.RequiredIfNoDef opt.RequiredIfNoDef = item.RequiredIfNoDef
} }
@ -183,11 +185,11 @@ func ParseWithFuncs(v interface{}, funcMap map[reflect.Type]ParserFunc, opts ...
ptrRef := reflect.ValueOf(v) ptrRef := reflect.ValueOf(v)
if ptrRef.Kind() != reflect.Ptr { if ptrRef.Kind() != reflect.Ptr {
return ErrNotAStructPtr return newAggregateError(NotStructPtrError{})
} }
ref := ptrRef.Elem() ref := ptrRef.Elem()
if ref.Kind() != reflect.Struct { if ref.Kind() != reflect.Struct {
return ErrNotAStructPtr return newAggregateError(NotStructPtrError{})
} }
parsers := defaultTypeParsers() parsers := defaultTypeParsers()
for k, v := range funcMap { for k, v := range funcMap {
@ -200,22 +202,22 @@ func ParseWithFuncs(v interface{}, funcMap map[reflect.Type]ParserFunc, opts ...
func doParse(ref reflect.Value, funcMap map[reflect.Type]ParserFunc, opts []Options) error { func doParse(ref reflect.Value, funcMap map[reflect.Type]ParserFunc, opts []Options) error {
refType := ref.Type() refType := ref.Type()
var agrErr aggregateError var agrErr AggregateError
for i := 0; i < refType.NumField(); i++ { for i := 0; i < refType.NumField(); i++ {
refField := ref.Field(i) refField := ref.Field(i)
refTypeField := refType.Field(i) refTypeField := refType.Field(i)
if err := doParseField(refField, refTypeField, funcMap, opts); err != nil { if err := doParseField(refField, refTypeField, funcMap, opts); err != nil {
if val, ok := err.(aggregateError); ok { if val, ok := err.(AggregateError); ok {
agrErr.errors = append(agrErr.errors, val.errors...) agrErr.Errors = append(agrErr.Errors, val.Errors...)
} else { } else {
agrErr.errors = append(agrErr.errors, err) agrErr.Errors = append(agrErr.Errors, err)
} }
} }
} }
if len(agrErr.errors) == 0 { if len(agrErr.Errors) == 0 {
return nil return nil
} }
@ -226,7 +228,7 @@ func doParseField(refField reflect.Value, refTypeField reflect.StructField, func
if !refField.CanSet() { if !refField.CanSet() {
return nil return nil
} }
if reflect.Ptr == refField.Kind() && refField.Elem().Kind() == reflect.Struct { if reflect.Ptr == refField.Kind() && !refField.IsNil() {
return ParseWithFuncs(refField.Interface(), funcMap, optsWithPrefix(refTypeField, opts)...) return ParseWithFuncs(refField.Interface(), funcMap, optsWithPrefix(refTypeField, opts)...)
} }
if reflect.Struct == refField.Kind() && refField.CanAddr() && refField.Type().Name() == "" { if reflect.Struct == refField.Kind() && refField.CanAddr() && refField.Type().Name() == "" {
@ -248,6 +250,19 @@ func doParseField(refField reflect.Value, refTypeField reflect.StructField, func
return nil return nil
} }
const underscore rune = '_'
func toEnvName(input string) string {
var output []rune
for i, c := range input {
if i > 0 && output[i-1] != underscore && c != underscore && unicode.ToUpper(c) == c {
output = append(output, underscore)
}
output = append(output, unicode.ToUpper(c))
}
return string(output)
}
func get(field reflect.StructField, opts []Options) (val string, err error) { func get(field reflect.StructField, opts []Options) (val string, err error) {
var exists bool var exists bool
var isDefault bool var isDefault bool
@ -258,6 +273,9 @@ func get(field reflect.StructField, opts []Options) (val string, err error) {
required := opts[0].RequiredIfNoDef required := opts[0].RequiredIfNoDef
prefix := opts[0].Prefix prefix := opts[0].Prefix
ownKey, tags := parseKeyForOption(field.Tag.Get(getTagName(opts))) ownKey, tags := parseKeyForOption(field.Tag.Get(getTagName(opts)))
if ownKey == "" && opts[0].UseFieldNameByDefault {
ownKey = toEnvName(field.Name)
}
key := prefix + ownKey key := prefix + ownKey
for _, tag := range tags { for _, tag := range tags {
switch tag { switch tag {
@ -272,7 +290,7 @@ func get(field reflect.StructField, opts []Options) (val string, err error) {
case "notEmpty": case "notEmpty":
notEmpty = true notEmpty = true
default: default:
return "", fmt.Errorf("tag option %q not supported", tag) return "", newNoSupportedTagOptionError(tag)
} }
} }
expand := strings.EqualFold(field.Tag.Get("envExpand"), "true") expand := strings.EqualFold(field.Tag.Get("envExpand"), "true")
@ -288,18 +306,18 @@ func get(field reflect.StructField, opts []Options) (val string, err error) {
} }
if required && !exists && len(ownKey) > 0 { if required && !exists && len(ownKey) > 0 {
return "", fmt.Errorf(`required environment variable %q is not set`, key) return "", newEnvVarIsNotSet(key)
} }
if notEmpty && val == "" { if notEmpty && val == "" {
return "", fmt.Errorf("environment variable %q should not be empty", key) return "", newEmptyEnvVarError(key)
} }
if loadFile && val != "" { if loadFile && val != "" {
filename := val filename := val
val, err = getFromFile(filename) val, err = getFromFile(filename)
if err != nil { if err != nil {
return "", fmt.Errorf(`could not load content of file "%s" from variable %s: %v`, filename, key, err) return "", newLoadFileContentError(filename, key, err)
} }
} }
@ -325,6 +343,8 @@ func getOr(key, defaultValue string, defExists bool, envs map[string]string) (st
switch { switch {
case (!exists || key == "") && defExists: case (!exists || key == "") && defExists:
return defaultValue, true, true return defaultValue, true, true
case exists && value == "" && defExists:
return defaultValue, true, true
case !exists: case !exists:
return "", false, false return "", false, false
} }
@ -369,8 +389,11 @@ func set(field reflect.Value, sf reflect.StructField, value string, funcMap map[
return nil return nil
} }
if field.Kind() == reflect.Slice { switch field.Kind() {
case reflect.Slice:
return handleSlice(field, value, sf, funcMap) return handleSlice(field, value, sf, funcMap)
case reflect.Map:
return handleMap(field, value, sf, funcMap)
} }
return newNoParserError(sf) return newNoParserError(sf)
@ -417,6 +440,54 @@ func handleSlice(field reflect.Value, value string, sf reflect.StructField, func
return nil return nil
} }
func handleMap(field reflect.Value, value string, sf reflect.StructField, funcMap map[reflect.Type]ParserFunc) error {
keyType := sf.Type.Key()
keyParserFunc, ok := funcMap[keyType]
if !ok {
keyParserFunc, ok = defaultBuiltInParsers[keyType.Kind()]
if !ok {
return newNoParserError(sf)
}
}
elemType := sf.Type.Elem()
elemParserFunc, ok := funcMap[elemType]
if !ok {
elemParserFunc, ok = defaultBuiltInParsers[elemType.Kind()]
if !ok {
return newNoParserError(sf)
}
}
separator := sf.Tag.Get("envSeparator")
if separator == "" {
separator = ","
}
result := reflect.MakeMap(sf.Type)
for _, part := range strings.Split(value, separator) {
pairs := strings.Split(part, ":")
if len(pairs) != 2 {
return newParseError(sf, fmt.Errorf(`%q should be in "key:value" format`, part))
}
key, err := keyParserFunc(pairs[0])
if err != nil {
return newParseError(sf, err)
}
elem, err := elemParserFunc(pairs[1])
if err != nil {
return newParseError(sf, err)
}
result.SetMapIndex(reflect.ValueOf(key).Convert(keyType), reflect.ValueOf(elem).Convert(elemType))
}
field.Set(result)
return nil
}
func asTextUnmarshaler(field reflect.Value) encoding.TextUnmarshaler { func asTextUnmarshaler(field reflect.Value) encoding.TextUnmarshaler {
if reflect.Ptr == field.Kind() { if reflect.Ptr == field.Kind() {
if field.IsNil() { if field.IsNil() {
@ -459,26 +530,6 @@ func parseTextUnmarshalers(field reflect.Value, data []string, sf reflect.Struct
return nil return nil
} }
func newParseError(sf reflect.StructField, err error) error {
return parseError{
sf: sf,
err: err,
}
}
type parseError struct {
sf reflect.StructField
err error
}
func (e parseError) Error() string {
return fmt.Sprintf(`parse error on field "%s" of type "%s": %v`, e.sf.Name, e.sf.Type, e.err)
}
func newNoParserError(sf reflect.StructField) error {
return fmt.Errorf(`no parser found for field "%s" of type "%s"`, sf.Name, sf.Type)
}
func optsWithPrefix(field reflect.StructField, opts []Options) []Options { func optsWithPrefix(field reflect.StructField, opts []Options) []Options {
subOpts := make([]Options, len(opts)) subOpts := make([]Options, len(opts))
copy(subOpts, opts) copy(subOpts, opts)
@ -487,18 +538,3 @@ func optsWithPrefix(field reflect.StructField, opts []Options) []Options {
} }
return subOpts return subOpts
} }
type aggregateError struct {
errors []error
}
func (e aggregateError) Error() string {
var sb strings.Builder
sb.WriteString("env:")
for _, err := range e.errors {
sb.WriteString(fmt.Sprintf(" %v;", err.Error()))
}
return strings.TrimRight(sb.String(), ";")
}

164
vendor/github.com/caarlos0/env/v7/error.go generated vendored Normal file
View File

@ -0,0 +1,164 @@
package env
import (
"fmt"
"reflect"
"strings"
)
// An aggregated error wrapper to combine gathered errors. This allows either to display all errors or convert them individually
// List of the available errors
// ParseError
// NotStructPtrError
// NoParserError
// NoSupportedTagOptionError
// EnvVarIsNotSetError
// EmptyEnvVarError
// LoadFileContentError
// ParseValueError
type AggregateError struct {
Errors []error
}
func newAggregateError(initErr error) error {
return AggregateError{
[]error{
initErr,
},
}
}
func (e AggregateError) Error() string {
var sb strings.Builder
sb.WriteString("env:")
for _, err := range e.Errors {
sb.WriteString(fmt.Sprintf(" %v;", err.Error()))
}
return strings.TrimRight(sb.String(), ";")
}
// Is conforms with errors.Is.
func (e AggregateError) Is(err error) bool {
for _, ie := range e.Errors {
if reflect.TypeOf(ie) == reflect.TypeOf(err) {
return true
}
}
return false
}
// The error occurs when it's impossible to convert the value for given type.
type ParseError struct {
Name string
Type reflect.Type
Err error
}
func newParseError(sf reflect.StructField, err error) error {
return ParseError{sf.Name, sf.Type, err}
}
func (e ParseError) Error() string {
return fmt.Sprintf(`parse error on field "%s" of type "%s": %v`, e.Name, e.Type, e.Err)
}
// The error occurs when pass something that is not a pointer to a Struct to Parse
type NotStructPtrError struct{}
func (e NotStructPtrError) Error() string {
return "expected a pointer to a Struct"
}
// This error occurs when there is no parser provided for given type
// Supported types and defaults: https://github.com/caarlos0/env#supported-types-and-defaults
// How to create a custom parser: https://github.com/caarlos0/env#custom-parser-funcs
type NoParserError struct {
Name string
Type reflect.Type
}
func newNoParserError(sf reflect.StructField) error {
return NoParserError{sf.Name, sf.Type}
}
func (e NoParserError) Error() string {
return fmt.Sprintf(`no parser found for field "%s" of type "%s"`, e.Name, e.Type)
}
// This error occurs when the given tag is not supported
// In-built supported tags: "", "file", "required", "unset", "notEmpty", "envDefault", "envExpand", "envSeparator"
// How to create a custom tag: https://github.com/caarlos0/env#changing-default-tag-name
type NoSupportedTagOptionError struct {
Tag string
}
func newNoSupportedTagOptionError(tag string) error {
return NoSupportedTagOptionError{tag}
}
func (e NoSupportedTagOptionError) Error() string {
return fmt.Sprintf("tag option %q not supported", e.Tag)
}
// This error occurs when the required variable is not set
// Read about required fields: https://github.com/caarlos0/env#required-fields
type EnvVarIsNotSetError struct {
Key string
}
func newEnvVarIsNotSet(key string) error {
return EnvVarIsNotSetError{key}
}
func (e EnvVarIsNotSetError) Error() string {
return fmt.Sprintf(`required environment variable %q is not set`, e.Key)
}
// This error occurs when the variable which must be not empty is existing but has an empty value
// Read about not empty fields: https://github.com/caarlos0/env#not-empty-fields
type EmptyEnvVarError struct {
Key string
}
func newEmptyEnvVarError(key string) error {
return EmptyEnvVarError{key}
}
func (e EmptyEnvVarError) Error() string {
return fmt.Sprintf("environment variable %q should not be empty", e.Key)
}
// This error occurs when it's impossible to load the value from the file
// Read about From file feature: https://github.com/caarlos0/env#from-file
type LoadFileContentError struct {
Filename string
Key string
Err error
}
func newLoadFileContentError(filename, key string, err error) error {
return LoadFileContentError{filename, key, err}
}
func (e LoadFileContentError) Error() string {
return fmt.Sprintf(`could not load content of file "%s" from variable %s: %v`, e.Filename, e.Key, e.Err)
}
// This error occurs when it's impossible to convert value using given parser
// Supported types and defaults: https://github.com/caarlos0/env#supported-types-and-defaults
// How to create a custom parser: https://github.com/caarlos0/env#custom-parser-funcs
type ParseValueError struct {
Msg string
Err error
}
func newParseValueError(message string, err error) error {
return ParseValueError{message, err}
}
func (e ParseValueError) Error() string {
return fmt.Sprintf("%s: %v", e.Msg, e.Err)
}

4
vendor/modules.txt vendored
View File

@ -1,9 +1,9 @@
# github.com/DATA-DOG/go-sqlmock v1.5.0 # github.com/DATA-DOG/go-sqlmock v1.5.0
## explicit ## explicit
github.com/DATA-DOG/go-sqlmock github.com/DATA-DOG/go-sqlmock
# github.com/caarlos0/env/v6 v6.10.1 # github.com/caarlos0/env/v7 v7.1.0
## explicit; go 1.17 ## explicit; go 1.17
github.com/caarlos0/env/v6 github.com/caarlos0/env/v7
# github.com/dustin/go-humanize v1.0.1 # github.com/dustin/go-humanize v1.0.1
## explicit; go 1.16 ## explicit; go 1.16
github.com/dustin/go-humanize github.com/dustin/go-humanize