mirror of
https://github.com/redhat-developer/odo.git
synced 2025-10-19 03:06:19 +03:00
Go: Bump github.com/sethvargo/go-envconfig from 0.9.0 to 1.0.0 (#7190)
* Go: Bump github.com/sethvargo/go-envconfig from 0.9.0 to 1.0.0 Bumps [github.com/sethvargo/go-envconfig](https://github.com/sethvargo/go-envconfig) from 0.9.0 to 1.0.0. - [Release notes](https://github.com/sethvargo/go-envconfig/releases) - [Commits](https://github.com/sethvargo/go-envconfig/compare/v0.9.0...v1.0.0) --- updated-dependencies: - dependency-name: github.com/sethvargo/go-envconfig dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> * Fix breaking change --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Armel Soro <asoro@redhat.com>
This commit is contained in:
339
vendor/github.com/sethvargo/go-envconfig/README.md
generated
vendored
339
vendor/github.com/sethvargo/go-envconfig/README.md
generated
vendored
@@ -1,23 +1,19 @@
|
||||
# Envconfig
|
||||
|
||||
[](https://pkg.go.dev/mod/github.com/sethvargo/go-envconfig)
|
||||
[](https://github.com/sethvargo/go-envconfig/actions?query=branch%3Amain+-event%3Aschedule)
|
||||
[][godoc]
|
||||
|
||||
Envconfig populates struct field values based on environment variables or
|
||||
arbitrary lookup functions. It supports pre-setting mutations, which is useful
|
||||
for things like converting values to uppercase, trimming whitespace, or looking
|
||||
up secrets.
|
||||
|
||||
**Note:** Versions prior to v0.2 used a different import path. This README and
|
||||
examples are for v0.2+.
|
||||
|
||||
## Usage
|
||||
|
||||
Define a struct with fields using the `env` tag:
|
||||
|
||||
```go
|
||||
type MyConfig struct {
|
||||
Port int `env:"PORT"`
|
||||
Port string `env:"PORT"`
|
||||
Username string `env:"USERNAME"`
|
||||
}
|
||||
```
|
||||
@@ -63,113 +59,141 @@ type MyConfig struct {
|
||||
}
|
||||
|
||||
type DatabaseConfig struct {
|
||||
Port int `env:"PORT"`
|
||||
Port string `env:"PORT"`
|
||||
Username string `env:"USERNAME"`
|
||||
}
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
Use the `env` struct tag to define configuration.
|
||||
Use the `env` struct tag to define configuration. See the [godoc][] for usage
|
||||
examples.
|
||||
|
||||
### Required
|
||||
- `required` - marks a field as required. If a field is required, decoding
|
||||
will error if the environment variable is unset.
|
||||
|
||||
If a field is required, processing will error if the environment variable is
|
||||
unset.
|
||||
```go
|
||||
type MyStruct struct {
|
||||
Port string `env:"PORT, required"`
|
||||
}
|
||||
```
|
||||
|
||||
```go
|
||||
type MyStruct struct {
|
||||
Port int `env:"PORT,required"`
|
||||
}
|
||||
```
|
||||
- `default` - sets the default value for the environment variable is not set.
|
||||
The environment variable must not be set (e.g. `unset PORT`). If the
|
||||
environment variable is the empty string, envconfig considers that a "value"
|
||||
and the default will **not** be used.
|
||||
|
||||
It is invalid to have a field as both `required` and `default`.
|
||||
You can also set the default value to the value from another field or a
|
||||
value from a different environment variable.
|
||||
|
||||
### Default
|
||||
```go
|
||||
type MyStruct struct {
|
||||
Port string `env:"PORT, default=5555"`
|
||||
User string `env:"USER, default=$CURRENT_USER"`
|
||||
}
|
||||
```
|
||||
|
||||
If an environment variable is not set, the field will be set to the default
|
||||
value. Note that the environment variable must not be set (e.g. `unset PORT`).
|
||||
If the environment variable is the empty string, that counts as a "value" and
|
||||
the default will not be used.
|
||||
- `prefix` - sets the prefix to use for looking up environment variable keys
|
||||
on child structs and fields. This is useful for shared configurations:
|
||||
|
||||
```go
|
||||
type MyStruct struct {
|
||||
Port int `env:"PORT,default=5555"`
|
||||
}
|
||||
```
|
||||
```go
|
||||
type RedisConfig struct {
|
||||
Host string `env:"REDIS_HOST"`
|
||||
User string `env:"REDIS_USER"`
|
||||
}
|
||||
|
||||
You can also set the default value to another field or value from the
|
||||
environment, for example:
|
||||
type ServerConfig struct {
|
||||
// CacheConfig will process values from $CACHE_REDIS_HOST and
|
||||
// $CACHE_REDIS respectively.
|
||||
CacheConfig *RedisConfig `env:", prefix=CACHE_"`
|
||||
|
||||
```go
|
||||
type MyStruct struct {
|
||||
DefaultPort int `env:"DEFAULT_PORT,default=5555"`
|
||||
Port int `env:"OVERRIDE_PORT,default=$DEFAULT_PORT"`
|
||||
}
|
||||
```
|
||||
// RateLimitConfig will process values from $RATE_LIMIT_REDIS_HOST and
|
||||
// $RATE_LIMIT_REDIS respectively.
|
||||
RateLimitConfig *RedisConfig `env:", prefix=RATE_LIMIT_"`
|
||||
}
|
||||
```
|
||||
|
||||
The value for `Port` will default to the value of `DEFAULT_PORT`.
|
||||
- `overwrite` - force overwriting existing non-zero struct values if the
|
||||
environment variable was provided.
|
||||
|
||||
It is invalid to have a field as both `required` and `default`.
|
||||
```go
|
||||
type MyStruct struct {
|
||||
Port string `env:"PORT, overwrite"`
|
||||
}
|
||||
```
|
||||
|
||||
### Prefix
|
||||
The rules for overwrite + default are:
|
||||
|
||||
For shared, embedded structs, you can define a prefix to use when processing
|
||||
struct values for that embed.
|
||||
- If the struct field has the zero value and a default is set:
|
||||
|
||||
```go
|
||||
type SharedConfig struct {
|
||||
Port int `env:"PORT,default=5555"`
|
||||
}
|
||||
- If no environment variable is specified, the struct field will be
|
||||
populated with the default value.
|
||||
|
||||
type Server1 struct {
|
||||
// This processes Port from $FOO_PORT.
|
||||
*SharedConfig `env:",prefix=FOO_"`
|
||||
}
|
||||
- If an environment variable is specified, the struct field will be
|
||||
populate with the environment variable value.
|
||||
|
||||
type Server2 struct {
|
||||
// This processes Port from $BAR_PORT.
|
||||
*SharedConfig `env:",prefix=BAR_"`
|
||||
}
|
||||
```
|
||||
- If the struct field has a non-zero value and a default is set:
|
||||
|
||||
It is invalid to specify a prefix on non-struct fields.
|
||||
- If no environment variable is specified, the struct field's existing
|
||||
value will be used (the default is ignored).
|
||||
|
||||
### Overwrite
|
||||
- If an environment variable is specified, the struct field's existing
|
||||
value will be overwritten with the environment variable value.
|
||||
|
||||
If overwrite is set, the value will be overwritten if there is an environment
|
||||
variable match regardless if the value is non-zero.
|
||||
- `delimiter` - choose a custom character to denote individual slice and map
|
||||
entries. The default value is the comma (`,`).
|
||||
|
||||
```go
|
||||
type MyStruct struct {
|
||||
Port int `env:"PORT,overwrite"`
|
||||
}
|
||||
```
|
||||
```go
|
||||
type MyStruct struct {
|
||||
MyVar []string `env:"MYVAR, delimiter=;"`
|
||||
```
|
||||
|
||||
The rules for overwrite + default are:
|
||||
```bash
|
||||
export MYVAR="a;b;c;d" # []string{"a", "b", "c", "d"}
|
||||
```
|
||||
|
||||
- If the struct field has the zero value and a default is set:
|
||||
- `separator` - choose a custom character to denote the separation between
|
||||
keys and values in map entries. The default value is the colon (`:`) Define
|
||||
a separator with `separator`:
|
||||
|
||||
- If no environment variable is specified, the struct field will be
|
||||
populated with the default value.
|
||||
```go
|
||||
type MyStruct struct {
|
||||
MyVar map[string]string `env:"MYVAR, separator=|"`
|
||||
}
|
||||
```
|
||||
|
||||
- If an environment variable is specified, the struct field will be
|
||||
populate with the environment variable value.
|
||||
```bash
|
||||
export MYVAR="a|b,c|d" # map[string]string{"a":"b", "c":"d"}
|
||||
```
|
||||
|
||||
- If the struct field has a non-zero value and a default is set:
|
||||
- `noinit` - do not initialize struct fields unless environment variables were
|
||||
provided. The default behavior is to deeply initialize all fields to their
|
||||
default (zero) value.
|
||||
|
||||
- If no environment variable is specified, the struct field's existing
|
||||
value will be used (the default is ignored).
|
||||
```go
|
||||
type MyStruct struct {
|
||||
MyVar *url.URL `env:"MYVAR, noinit"`
|
||||
}
|
||||
```
|
||||
|
||||
- If an environment variable is specified, the struct field's existing
|
||||
value will be overwritten with the environment variable value.
|
||||
- `decodeunset` - force envconfig to run decoders even on unset environment
|
||||
variable values. The default behavior is to skip running decoders on unset
|
||||
environment variable values.
|
||||
|
||||
```go
|
||||
type MyStruct struct {
|
||||
MyVar *url.URL `env:"MYVAR, decodeunset"`
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## Complex Types
|
||||
## Decoding
|
||||
|
||||
> [!NOTE]
|
||||
>
|
||||
> Complex types are only decoded or unmarshalled when the environment variable
|
||||
> is defined or a default value is specified.
|
||||
|
||||
**Note:** Complex types are only decoded or unmarshalled when the environment
|
||||
variable is defined or a default is specified. The decoding/unmarshalling
|
||||
functions are _not_ invoked when a value is not defined.
|
||||
|
||||
### Durations
|
||||
|
||||
@@ -186,14 +210,18 @@ type MyStruct struct {
|
||||
export MYVAR="10m" # 10 * time.Minute
|
||||
```
|
||||
|
||||
|
||||
### TextUnmarshaler / BinaryUnmarshaler
|
||||
|
||||
Types that implement `TextUnmarshaler` or `BinaryUnmarshaler` are processed as such.
|
||||
Types that implement `TextUnmarshaler` or `BinaryUnmarshaler` are processed as
|
||||
such.
|
||||
|
||||
|
||||
### json.Unmarshaler
|
||||
|
||||
Types that implement `json.Unmarshaler` are processed as such.
|
||||
|
||||
|
||||
### gob.Decoder
|
||||
|
||||
Types that implement `gob.Decoder` are processed as such.
|
||||
@@ -201,7 +229,7 @@ Types that implement `gob.Decoder` are processed as such.
|
||||
|
||||
### Slices
|
||||
|
||||
Slices are specified as comma-separated values:
|
||||
Slices are specified as comma-separated values.
|
||||
|
||||
```go
|
||||
type MyStruct struct {
|
||||
@@ -213,20 +241,10 @@ type MyStruct struct {
|
||||
export MYVAR="a,b,c,d" # []string{"a", "b", "c", "d"}
|
||||
```
|
||||
|
||||
Define a custom delimiter with `delimiter`:
|
||||
|
||||
```go
|
||||
type MyStruct struct {
|
||||
MyVar []string `env:"MYVAR,delimiter=;"`
|
||||
```
|
||||
|
||||
```bash
|
||||
export MYVAR="a;b;c;d" # []string{"a", "b", "c", "d"}
|
||||
```
|
||||
|
||||
Note that byte slices are special cased and interpreted as strings from the
|
||||
environment.
|
||||
|
||||
|
||||
### Maps
|
||||
|
||||
Maps are specified as comma-separated key:value pairs:
|
||||
@@ -241,29 +259,6 @@ type MyStruct struct {
|
||||
export MYVAR="a:b,c:d" # map[string]string{"a":"b", "c":"d"}
|
||||
```
|
||||
|
||||
Define a custom delimiter with `delimiter`:
|
||||
|
||||
```go
|
||||
type MyStruct struct {
|
||||
MyVar map[string]string `env:"MYVAR,delimiter=;"`
|
||||
```
|
||||
|
||||
```bash
|
||||
export MYVAR="a:b;c:d" # map[string]string{"a":"b", "c":"d"}
|
||||
```
|
||||
|
||||
Define a separator with `separator`:
|
||||
|
||||
```go
|
||||
type MyStruct struct {
|
||||
MyVar map[string]string `env:"MYVAR,separator=|"`
|
||||
}
|
||||
```
|
||||
|
||||
```bash
|
||||
export MYVAR="a|b,c|d" # map[string]string{"a":"b", "c":"d"}
|
||||
```
|
||||
|
||||
|
||||
### Structs
|
||||
|
||||
@@ -275,102 +270,12 @@ the non-nil value. To change this behavior, see
|
||||
[Initialization](#Initialization).
|
||||
|
||||
|
||||
### Custom
|
||||
### Custom Decoders
|
||||
|
||||
You can also [define your own decoder](#Extension).
|
||||
You can also define your own decoders. See the [godoc][godoc] for more
|
||||
information.
|
||||
|
||||
|
||||
## Prefixing
|
||||
|
||||
You can define a custom prefix using the `PrefixLookuper`. This will lookup
|
||||
values in the environment by prefixing the keys with the provided value:
|
||||
|
||||
```go
|
||||
type MyStruct struct {
|
||||
MyVar string `env:"MYVAR"`
|
||||
}
|
||||
```
|
||||
|
||||
```go
|
||||
// Process variables, but look for the "APP_" prefix.
|
||||
l := envconfig.PrefixLookuper("APP_", envconfig.OsLookuper())
|
||||
if err := envconfig.ProcessWith(ctx, &c, l); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
```
|
||||
|
||||
```bash
|
||||
export APP_MYVAR="foo"
|
||||
```
|
||||
|
||||
## Initialization
|
||||
|
||||
By default, all pointers, slices, and maps are initialized (allocated) so they
|
||||
are not `nil`. To disable this behavior, use the tag the field as `noinit`:
|
||||
|
||||
```go
|
||||
type MyStruct struct {
|
||||
// Without `noinit`, DeleteUser would be initialized to the default boolean
|
||||
// value. With `noinit`, if the environment variable is not given, the value
|
||||
// is kept as uninitialized (nil).
|
||||
DeleteUser *bool `env:"DELETE_USER, noinit"`
|
||||
}
|
||||
```
|
||||
|
||||
This also applies to nested fields in a struct:
|
||||
|
||||
```go
|
||||
type ParentConfig struct {
|
||||
// Without `noinit` tag, `Child` would be set to `&ChildConfig{}` whether
|
||||
// or not `FIELD` is set in the env var.
|
||||
// With `noinit`, `Child` would stay nil if `FIELD` is not set in the env var.
|
||||
Child *ChildConfig `env:",noinit"`
|
||||
}
|
||||
|
||||
type ChildConfig struct {
|
||||
Field string `env:"FIELD"`
|
||||
}
|
||||
```
|
||||
|
||||
The `noinit` tag is only applicable for pointer, slice, and map fields. Putting
|
||||
the tag on a different type will return an error.
|
||||
|
||||
|
||||
## Extension
|
||||
|
||||
All built-in types are supported except `Func` and `Chan`. If you need to define
|
||||
a custom decoder, implement the `Decoder` interface:
|
||||
|
||||
```go
|
||||
type MyStruct struct {
|
||||
field string
|
||||
}
|
||||
|
||||
func (v *MyStruct) EnvDecode(val string) error {
|
||||
v.field = fmt.Sprintf("PREFIX-%s", val)
|
||||
return nil
|
||||
}
|
||||
```
|
||||
|
||||
If you need to modify environment variable values before processing, you can
|
||||
specify a custom `Mutator`:
|
||||
|
||||
```go
|
||||
type Config struct {
|
||||
Password `env:"PASSWORD"`
|
||||
}
|
||||
|
||||
func resolveSecretFunc(ctx context.Context, key, value string) (string, error) {
|
||||
if strings.HasPrefix(value, "secret://") {
|
||||
return secretmanager.Resolve(ctx, value) // example
|
||||
}
|
||||
return value, nil
|
||||
}
|
||||
|
||||
var config Config
|
||||
envconfig.ProcessWith(ctx, &config, envconfig.OsLookuper(), resolveSecretFunc)
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
Relying on the environment in tests can be troublesome because environment
|
||||
@@ -384,7 +289,9 @@ lookuper := envconfig.MapLookuper(map[string]string{
|
||||
})
|
||||
|
||||
var config Config
|
||||
envconfig.ProcessWith(ctx, &config, lookuper)
|
||||
envconfig.ProcessWith(ctx, &config, &envconfig.Config{
|
||||
Lookuper: lookuper,
|
||||
})
|
||||
```
|
||||
|
||||
Now you can parallelize all your tests by providing a map for the lookup
|
||||
@@ -395,20 +302,4 @@ You can also combine multiple lookupers with `MultiLookuper`. See the GoDoc for
|
||||
more information and examples.
|
||||
|
||||
|
||||
## Inspiration
|
||||
|
||||
This library is conceptually similar to [kelseyhightower/envconfig](https://github.com/kelseyhightower/envconfig), with the following
|
||||
major behavioral differences:
|
||||
|
||||
- Adds support for specifying a custom lookup function (such as a map), which
|
||||
is useful for testing.
|
||||
|
||||
- Only populates fields if they contain zero or nil values if `overwrite` is
|
||||
unset. This means you can pre-initialize a struct and any pre-populated
|
||||
fields will not be overwritten during processing.
|
||||
|
||||
- Support for interpolation. The default value for a field can be the value of
|
||||
another field.
|
||||
|
||||
- Support for arbitrary mutators that change/resolve data before type
|
||||
conversion.
|
||||
[godoc]: https://pkg.go.dev/mod/github.com/sethvargo/go-envconfig
|
||||
|
||||
369
vendor/github.com/sethvargo/go-envconfig/envconfig.go
generated
vendored
369
vendor/github.com/sethvargo/go-envconfig/envconfig.go
generated
vendored
@@ -46,22 +46,7 @@
|
||||
//
|
||||
// export MYVAR="a:b,c:d" // map[string]string{"a":"b", "c":"d"}
|
||||
//
|
||||
// If you need to modify environment variable values before processing, you can
|
||||
// specify a custom mutator:
|
||||
//
|
||||
// type Config struct {
|
||||
// Password `env:"PASSWORD_SECRET"`
|
||||
// }
|
||||
//
|
||||
// func resolveSecretFunc(ctx context.Context, key, value string) (string, error) {
|
||||
// if strings.HasPrefix(value, "secret://") {
|
||||
// return secretmanager.Resolve(ctx, value) // example
|
||||
// }
|
||||
// return value, nil
|
||||
// }
|
||||
//
|
||||
// var config Config
|
||||
// ProcessWith(&config, OsLookuper(), resolveSecretFunc)
|
||||
// For more configuration options and examples, see the documentation.
|
||||
package envconfig
|
||||
|
||||
import (
|
||||
@@ -82,39 +67,37 @@ import (
|
||||
const (
|
||||
envTag = "env"
|
||||
|
||||
optDefault = "default="
|
||||
optDelimiter = "delimiter="
|
||||
optNoInit = "noinit"
|
||||
optOverwrite = "overwrite"
|
||||
optPrefix = "prefix="
|
||||
optRequired = "required"
|
||||
optSeparator = "separator="
|
||||
|
||||
defaultDelimiter = ","
|
||||
defaultSeparator = ":"
|
||||
optDecodeUnset = "decodeunset"
|
||||
optDefault = "default="
|
||||
optDelimiter = "delimiter="
|
||||
optNoInit = "noinit"
|
||||
optOverwrite = "overwrite"
|
||||
optPrefix = "prefix="
|
||||
optRequired = "required"
|
||||
optSeparator = "separator="
|
||||
)
|
||||
|
||||
// Error is a custom error type for errors returned by envconfig.
|
||||
type Error string
|
||||
// internalError is a custom error type for errors returned by envconfig.
|
||||
type internalError string
|
||||
|
||||
// Error implements error.
|
||||
func (e Error) Error() string {
|
||||
func (e internalError) Error() string {
|
||||
return string(e)
|
||||
}
|
||||
|
||||
const (
|
||||
ErrInvalidEnvvarName = Error("invalid environment variable name")
|
||||
ErrInvalidMapItem = Error("invalid map item")
|
||||
ErrLookuperNil = Error("lookuper cannot be nil")
|
||||
ErrMissingKey = Error("missing key")
|
||||
ErrMissingRequired = Error("missing required value")
|
||||
ErrNoInitNotPtr = Error("field must be a pointer to have noinit")
|
||||
ErrNotPtr = Error("input must be a pointer")
|
||||
ErrNotStruct = Error("input must be a struct")
|
||||
ErrPrefixNotStruct = Error("prefix is only valid on struct types")
|
||||
ErrPrivateField = Error("cannot parse private fields")
|
||||
ErrRequiredAndDefault = Error("field cannot be required and have a default value")
|
||||
ErrUnknownOption = Error("unknown option")
|
||||
ErrInvalidEnvvarName = internalError("invalid environment variable name")
|
||||
ErrInvalidMapItem = internalError("invalid map item")
|
||||
ErrLookuperNil = internalError("lookuper cannot be nil")
|
||||
ErrMissingKey = internalError("missing key")
|
||||
ErrMissingRequired = internalError("missing required value")
|
||||
ErrNoInitNotPtr = internalError("field must be a pointer to have noinit")
|
||||
ErrNotPtr = internalError("input must be a pointer")
|
||||
ErrNotStruct = internalError("input must be a struct")
|
||||
ErrPrefixNotStruct = internalError("prefix is only valid on struct types")
|
||||
ErrPrivateField = internalError("cannot parse private fields")
|
||||
ErrRequiredAndDefault = internalError("field cannot be required and have a default value")
|
||||
ErrUnknownOption = internalError("unknown option")
|
||||
)
|
||||
|
||||
// Lookuper is an interface that provides a lookup for a string-based key.
|
||||
@@ -188,7 +171,24 @@ type prefixLookuper struct {
|
||||
}
|
||||
|
||||
func (p *prefixLookuper) Lookup(key string) (string, bool) {
|
||||
return p.l.Lookup(p.prefix + key)
|
||||
return p.l.Lookup(p.Key(key))
|
||||
}
|
||||
|
||||
func (p *prefixLookuper) Key(key string) string {
|
||||
return p.prefix + key
|
||||
}
|
||||
|
||||
func (p *prefixLookuper) Unwrap() Lookuper {
|
||||
l := p.l
|
||||
for v, ok := l.(unwrappableLookuper); ok; {
|
||||
l = v.Unwrap()
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
||||
// unwrappableLookuper is a lookuper that can return the underlying lookuper.
|
||||
type unwrappableLookuper interface {
|
||||
Unwrap() Lookuper
|
||||
}
|
||||
|
||||
// MultiLookuper wraps a collection of lookupers. It does not combine them, and
|
||||
@@ -197,6 +197,12 @@ func MultiLookuper(lookupers ...Lookuper) Lookuper {
|
||||
return &multiLookuper{ls: lookupers}
|
||||
}
|
||||
|
||||
// keyedLookuper is an extension to the [Lookuper] interface that returns the
|
||||
// underlying key (used by the [PrefixLookuper] or custom implementations).
|
||||
type keyedLookuper interface {
|
||||
Key(key string) string
|
||||
}
|
||||
|
||||
// Decoder is an interface that custom types/fields can implement to control how
|
||||
// decoding takes place. For example:
|
||||
//
|
||||
@@ -209,59 +215,95 @@ type Decoder interface {
|
||||
EnvDecode(val string) error
|
||||
}
|
||||
|
||||
// MutatorFunc is a function that mutates a given value before it is passed
|
||||
// along for processing. This is useful if you want to mutate the environment
|
||||
// variable value before it's converted to the proper type.
|
||||
type MutatorFunc func(ctx context.Context, k, v string) (string, error)
|
||||
|
||||
// options are internal options for decoding.
|
||||
type options struct {
|
||||
Default string
|
||||
Delimiter string
|
||||
Prefix string
|
||||
Separator string
|
||||
NoInit bool
|
||||
Overwrite bool
|
||||
Required bool
|
||||
Default string
|
||||
Delimiter string
|
||||
Prefix string
|
||||
Separator string
|
||||
NoInit bool
|
||||
Overwrite bool
|
||||
DecodeUnset bool
|
||||
Required bool
|
||||
}
|
||||
|
||||
// Process processes the struct using the environment. See [ProcessWith] for a
|
||||
// more customizable version.
|
||||
func Process(ctx context.Context, i interface{}) error {
|
||||
return ProcessWith(ctx, i, OsLookuper())
|
||||
// Config represent inputs to the envconfig decoding.
|
||||
type Config struct {
|
||||
// Target is the destination structure to decode. This value is required, and
|
||||
// it must be a pointer to a struct.
|
||||
Target any
|
||||
|
||||
// Lookuper is the lookuper implementation to use. If not provided, it
|
||||
// defaults to the OS Lookuper.
|
||||
Lookuper Lookuper
|
||||
|
||||
// DefaultDelimiter is the default value to use for the delimiter in maps and
|
||||
// slices. This can be overridden on a per-field basis, which takes
|
||||
// precedence. The default value is ",".
|
||||
DefaultDelimiter string
|
||||
|
||||
// DefaultSeparator is the default value to use for the separator in maps.
|
||||
// This can be overridden on a per-field basis, which takes precedence. The
|
||||
// default value is ":".
|
||||
DefaultSeparator string
|
||||
|
||||
// DefaultNoInit is the default value for skipping initialization of
|
||||
// unprovided fields. The default value is false (deeply initialize all
|
||||
// fields and nested structs).
|
||||
DefaultNoInit bool
|
||||
|
||||
// DefaultOverwrite is the default value for overwriting an existing value set
|
||||
// on the struct before processing. The default value is false.
|
||||
DefaultOverwrite bool
|
||||
|
||||
// DefaultDecodeUnset is the default value for running decoders even when no
|
||||
// value was given for the environment variable.
|
||||
DefaultDecodeUnset bool
|
||||
|
||||
// DefaultRequired is the default value for marking a field as required. The
|
||||
// default value is false.
|
||||
DefaultRequired bool
|
||||
|
||||
// Mutators is an optiona list of mutators to apply to lookups.
|
||||
Mutators []Mutator
|
||||
}
|
||||
|
||||
// ProcessWith processes the given interface with the given lookuper. See the
|
||||
// package-level documentation for specific examples and behaviors.
|
||||
func ProcessWith(ctx context.Context, i interface{}, l Lookuper, fns ...MutatorFunc) error {
|
||||
return processWith(ctx, i, l, false, fns...)
|
||||
// Process decodes the struct using values from environment variables. See
|
||||
// [ProcessWith] for a more customizable version.
|
||||
func Process(ctx context.Context, i any, mus ...Mutator) error {
|
||||
return ProcessWith(ctx, &Config{
|
||||
Target: i,
|
||||
Mutators: mus,
|
||||
})
|
||||
}
|
||||
|
||||
// ExtractDefaults is a helper that returns a fully-populated struct with the
|
||||
// default values resolved. This is helpful when you want to return a constant
|
||||
// "default" configuration that is not affected by the user's environment.
|
||||
//
|
||||
// type Config struct {
|
||||
// Port string `env:"PORT,default=8080"`
|
||||
// }
|
||||
//
|
||||
// func DefaultConfig() *Config {
|
||||
// var cfg Config
|
||||
// if err := envconfig.ExtractDefaults(ctx, &cfg); err != nil {
|
||||
// panic("failed to extract default config: %s" + err.Error())
|
||||
// }
|
||||
// return &cfg
|
||||
// }
|
||||
//
|
||||
// This is effectively the same as calling [ProcessWith] with an empty
|
||||
// [MapLookuper].
|
||||
func ExtractDefaults(ctx context.Context, i interface{}, fns ...MutatorFunc) error {
|
||||
return processWith(ctx, i, MapLookuper(nil), false, fns...)
|
||||
// ProcessWith executes the decoding process using the provided [Config].
|
||||
func ProcessWith(ctx context.Context, c *Config) error {
|
||||
if c == nil {
|
||||
c = new(Config)
|
||||
}
|
||||
|
||||
if c.Lookuper == nil {
|
||||
c.Lookuper = OsLookuper()
|
||||
}
|
||||
|
||||
// Deep copy the slice and remove any nil mutators.
|
||||
var mus []Mutator
|
||||
for _, m := range c.Mutators {
|
||||
if m != nil {
|
||||
mus = append(mus, m)
|
||||
}
|
||||
}
|
||||
c.Mutators = mus
|
||||
|
||||
return processWith(ctx, c)
|
||||
}
|
||||
|
||||
// processWith is a helper that captures whether the parent wanted
|
||||
// initialization.
|
||||
func processWith(ctx context.Context, i interface{}, l Lookuper, parentNoInit bool, fns ...MutatorFunc) error {
|
||||
// processWith is a helper that retains configuration from the parent structs.
|
||||
func processWith(ctx context.Context, c *Config) error {
|
||||
i := c.Target
|
||||
|
||||
l := c.Lookuper
|
||||
if l == nil {
|
||||
return ErrLookuperNil
|
||||
}
|
||||
@@ -278,6 +320,24 @@ func processWith(ctx context.Context, i interface{}, l Lookuper, parentNoInit bo
|
||||
|
||||
t := e.Type()
|
||||
|
||||
structDelimiter := c.DefaultDelimiter
|
||||
if structDelimiter == "" {
|
||||
structDelimiter = ","
|
||||
}
|
||||
|
||||
structNoInit := c.DefaultNoInit
|
||||
|
||||
structSeparator := c.DefaultSeparator
|
||||
if structSeparator == "" {
|
||||
structSeparator = ":"
|
||||
}
|
||||
|
||||
structOverwrite := c.DefaultOverwrite
|
||||
structDecodeUnset := c.DefaultDecodeUnset
|
||||
structRequired := c.DefaultRequired
|
||||
|
||||
mutators := c.Mutators
|
||||
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
ef := e.Field(i)
|
||||
tf := t.Field(i)
|
||||
@@ -308,7 +368,21 @@ func processWith(ctx context.Context, i interface{}, l Lookuper, parentNoInit bo
|
||||
ef.Kind() != reflect.UnsafePointer {
|
||||
return fmt.Errorf("%s: %w", tf.Name, ErrNoInitNotPtr)
|
||||
}
|
||||
shouldNotInit := opts.NoInit || parentNoInit
|
||||
|
||||
// Compute defaults from local tags.
|
||||
delimiter := structDelimiter
|
||||
if v := opts.Delimiter; v != "" {
|
||||
delimiter = v
|
||||
}
|
||||
separator := structSeparator
|
||||
if v := opts.Separator; v != "" {
|
||||
separator = v
|
||||
}
|
||||
|
||||
noInit := structNoInit || opts.NoInit
|
||||
overwrite := structOverwrite || opts.Overwrite
|
||||
decodeUnset := structDecodeUnset || opts.DecodeUnset
|
||||
required := structRequired || opts.Required
|
||||
|
||||
isNilStructPtr := false
|
||||
setNilStruct := func(v reflect.Value) {
|
||||
@@ -319,7 +393,7 @@ func processWith(ctx context.Context, i interface{}, l Lookuper, parentNoInit bo
|
||||
// If a struct (after traversal) equals to the empty value, it means
|
||||
// nothing was changed in any sub-fields. With the noinit opt, we skip
|
||||
// setting the empty value to the original struct pointer (keep it nil).
|
||||
if !reflect.DeepEqual(v.Interface(), empty) || !shouldNotInit {
|
||||
if !reflect.DeepEqual(v.Interface(), empty) || !noInit {
|
||||
origin.Set(v)
|
||||
}
|
||||
}
|
||||
@@ -354,18 +428,20 @@ func processWith(ctx context.Context, i interface{}, l Lookuper, parentNoInit bo
|
||||
// Lookup the value, ignoring an error if the key isn't defined. This is
|
||||
// required for nested structs that don't declare their own `env` keys,
|
||||
// but have internal fields with an `env` defined.
|
||||
val, _, _, err := lookup(key, opts, l)
|
||||
val, found, usedDefault, err := lookup(key, required, opts.Default, l)
|
||||
if err != nil && !errors.Is(err, ErrMissingKey) {
|
||||
return fmt.Errorf("%s: %w", tf.Name, err)
|
||||
}
|
||||
|
||||
if ok, err := processAsDecoder(val, ef); ok {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if found || usedDefault || decodeUnset {
|
||||
if ok, err := processAsDecoder(val, ef); ok {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
setNilStruct(ef)
|
||||
continue
|
||||
setNilStruct(ef)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
plu := l
|
||||
@@ -373,7 +449,16 @@ func processWith(ctx context.Context, i interface{}, l Lookuper, parentNoInit bo
|
||||
plu = PrefixLookuper(opts.Prefix, l)
|
||||
}
|
||||
|
||||
if err := processWith(ctx, ef.Interface(), plu, shouldNotInit, fns...); err != nil {
|
||||
if err := processWith(ctx, &Config{
|
||||
Target: ef.Interface(),
|
||||
Lookuper: plu,
|
||||
DefaultDelimiter: delimiter,
|
||||
DefaultSeparator: separator,
|
||||
DefaultNoInit: noInit,
|
||||
DefaultOverwrite: overwrite,
|
||||
DefaultRequired: required,
|
||||
Mutators: mutators,
|
||||
}); err != nil {
|
||||
return fmt.Errorf("%s: %w", tf.Name, err)
|
||||
}
|
||||
|
||||
@@ -394,11 +479,11 @@ func processWith(ctx context.Context, i interface{}, l Lookuper, parentNoInit bo
|
||||
|
||||
// The field already has a non-zero value and overwrite is false, do not
|
||||
// overwrite.
|
||||
if (pointerWasSet || !ef.IsZero()) && !opts.Overwrite {
|
||||
if (pointerWasSet || !ef.IsZero()) && !overwrite {
|
||||
continue
|
||||
}
|
||||
|
||||
val, found, usedDefault, err := lookup(key, opts, l)
|
||||
val, found, usedDefault, err := lookup(key, required, opts.Default, l)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %w", tf.Name, err)
|
||||
}
|
||||
@@ -414,28 +499,27 @@ func processWith(ctx context.Context, i interface{}, l Lookuper, parentNoInit bo
|
||||
// type conversions. They always resolve to a string (or error), so we don't
|
||||
// call mutators when the environment variable was not set.
|
||||
if found || usedDefault {
|
||||
for _, fn := range fns {
|
||||
if fn != nil {
|
||||
val, err = fn(ctx, key, val)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %w", tf.Name, err)
|
||||
}
|
||||
originalKey := key
|
||||
resolvedKey := originalKey
|
||||
if keyer, ok := l.(keyedLookuper); ok {
|
||||
resolvedKey = keyer.Key(resolvedKey)
|
||||
}
|
||||
originalValue := val
|
||||
stop := false
|
||||
|
||||
for _, mu := range mutators {
|
||||
val, stop, err = mu.EnvMutate(ctx, originalKey, resolvedKey, originalValue, val)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %w", tf.Name, err)
|
||||
}
|
||||
if stop {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If Delimiter is not defined set it to ","
|
||||
if opts.Delimiter == "" {
|
||||
opts.Delimiter = defaultDelimiter
|
||||
}
|
||||
|
||||
// If Separator is not defined set it to ":"
|
||||
if opts.Separator == "" {
|
||||
opts.Separator = defaultSeparator
|
||||
}
|
||||
|
||||
// Set value.
|
||||
if err := processField(val, ef, opts.Delimiter, opts.Separator, opts.NoInit); err != nil {
|
||||
if err := processField(val, ef, delimiter, separator, noInit); err != nil {
|
||||
return fmt.Errorf("%s(%q): %w", tf.Name, val, err)
|
||||
}
|
||||
}
|
||||
@@ -443,10 +527,24 @@ func processWith(ctx context.Context, i interface{}, l Lookuper, parentNoInit bo
|
||||
return nil
|
||||
}
|
||||
|
||||
// SplitString splits the given string on the provided rune, unless the rune is
|
||||
// escaped by the escape character.
|
||||
func splitString(s string, on string, esc string) []string {
|
||||
a := strings.Split(s, on)
|
||||
|
||||
for i := len(a) - 2; i >= 0; i-- {
|
||||
if strings.HasSuffix(a[i], esc) {
|
||||
a[i] = a[i][:len(a[i])-len(esc)] + on + a[i+1]
|
||||
a = append(a[:i+1], a[i+2:]...)
|
||||
}
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// keyAndOpts parses the given tag value (e.g. env:"foo,required") and
|
||||
// returns the key name and options as a list.
|
||||
func keyAndOpts(tag string) (string, *options, error) {
|
||||
parts := strings.Split(tag, ",")
|
||||
parts := splitString(tag, ",", "\\")
|
||||
key, tagOpts := strings.TrimSpace(parts[0]), parts[1:]
|
||||
|
||||
if key != "" && !validateEnvName(key) {
|
||||
@@ -458,20 +556,24 @@ func keyAndOpts(tag string) (string, *options, error) {
|
||||
LOOP:
|
||||
for i, o := range tagOpts {
|
||||
o = strings.TrimLeftFunc(o, unicode.IsSpace)
|
||||
search := strings.ToLower(o)
|
||||
|
||||
switch {
|
||||
case o == optOverwrite:
|
||||
case search == optDecodeUnset:
|
||||
opts.DecodeUnset = true
|
||||
case search == optOverwrite:
|
||||
opts.Overwrite = true
|
||||
case o == optRequired:
|
||||
case search == optRequired:
|
||||
opts.Required = true
|
||||
case o == optNoInit:
|
||||
case search == optNoInit:
|
||||
opts.NoInit = true
|
||||
case strings.HasPrefix(o, optPrefix):
|
||||
case strings.HasPrefix(search, optPrefix):
|
||||
opts.Prefix = strings.TrimPrefix(o, optPrefix)
|
||||
case strings.HasPrefix(o, optDelimiter):
|
||||
case strings.HasPrefix(search, optDelimiter):
|
||||
opts.Delimiter = strings.TrimPrefix(o, optDelimiter)
|
||||
case strings.HasPrefix(o, optSeparator):
|
||||
case strings.HasPrefix(search, optSeparator):
|
||||
opts.Separator = strings.TrimPrefix(o, optSeparator)
|
||||
case strings.HasPrefix(o, optDefault):
|
||||
case strings.HasPrefix(search, optDefault):
|
||||
// If a default value was given, assume everything after is the provided
|
||||
// value, including comma-seprated items.
|
||||
o = strings.TrimLeft(strings.Join(tagOpts[i:], ","), " ")
|
||||
@@ -489,7 +591,7 @@ LOOP:
|
||||
// first boolean parameter indicates whether the value was found in the
|
||||
// lookuper. The second boolean parameter indicates whether the default value
|
||||
// was used.
|
||||
func lookup(key string, opts *options, l Lookuper) (string, bool, bool, error) {
|
||||
func lookup(key string, required bool, defaultValue string, l Lookuper) (string, bool, bool, error) {
|
||||
if key == "" {
|
||||
// The struct has something like `env:",required"`, which is likely a
|
||||
// mistake. We could try to infer the envvar from the field name, but that
|
||||
@@ -497,7 +599,7 @@ func lookup(key string, opts *options, l Lookuper) (string, bool, bool, error) {
|
||||
return "", false, false, ErrMissingKey
|
||||
}
|
||||
|
||||
if opts.Required && opts.Default != "" {
|
||||
if required && defaultValue != "" {
|
||||
// Having a default value on a required value doesn't make sense.
|
||||
return "", false, false, ErrRequiredAndDefault
|
||||
}
|
||||
@@ -505,19 +607,24 @@ func lookup(key string, opts *options, l Lookuper) (string, bool, bool, error) {
|
||||
// Lookup value.
|
||||
val, found := l.Lookup(key)
|
||||
if !found {
|
||||
if opts.Required {
|
||||
if pl, ok := l.(*prefixLookuper); ok {
|
||||
key = pl.prefix + key
|
||||
if required {
|
||||
if keyer, ok := l.(keyedLookuper); ok {
|
||||
key = keyer.Key(key)
|
||||
}
|
||||
|
||||
return "", false, false, fmt.Errorf("%w: %s", ErrMissingRequired, key)
|
||||
}
|
||||
|
||||
if opts.Default != "" {
|
||||
if defaultValue != "" {
|
||||
// Expand the default value. This allows for a default value that maps to
|
||||
// a different variable.
|
||||
val = os.Expand(opts.Default, func(i string) string {
|
||||
s, ok := l.Lookup(i)
|
||||
// a different environment variable.
|
||||
val = os.Expand(defaultValue, func(i string) string {
|
||||
lookuper := l
|
||||
if v, ok := lookuper.(unwrappableLookuper); ok {
|
||||
lookuper = v.Unwrap()
|
||||
}
|
||||
|
||||
s, ok := lookuper.Lookup(i)
|
||||
if ok {
|
||||
return s
|
||||
}
|
||||
|
||||
76
vendor/github.com/sethvargo/go-envconfig/mutator.go
generated
vendored
Normal file
76
vendor/github.com/sethvargo/go-envconfig/mutator.go
generated
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
// Copyright The envconfig Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package envconfig
|
||||
|
||||
import "context"
|
||||
|
||||
// Mutator is the interface for a mutator function. Mutators act like middleware
|
||||
// and alter values for subsequent processing. This is useful if you want to
|
||||
// mutate the environment variable value before it's converted to the proper
|
||||
// type.
|
||||
//
|
||||
// Mutators are only called on defined values (or when decodeunset is true).
|
||||
type Mutator interface {
|
||||
// EnvMutate is called to alter the environment variable value.
|
||||
//
|
||||
// - `originalKey` is the unmodified environment variable name as it was defined
|
||||
// on the struct.
|
||||
//
|
||||
// - `resolvedKey` is the fully-resolved environment variable name, which may
|
||||
// include prefixes or modifications from processing. When there are
|
||||
// no modifications, this will be equivalent to `originalKey`.
|
||||
//
|
||||
// - `originalValue` is the unmodified environment variable's value before any
|
||||
// mutations were run.
|
||||
//
|
||||
// - `currentValue` is the currently-resolved value, which may have been
|
||||
// modified by previous mutators and may be modified in the future by
|
||||
// subsequent mutators in the stack.
|
||||
//
|
||||
// The function returns (in order):
|
||||
//
|
||||
// - The new value to use in both future mutations and final processing.
|
||||
//
|
||||
// - A boolean which indicates whether future mutations in the stack should be
|
||||
// applied.
|
||||
//
|
||||
// - Any errors that occurred.
|
||||
//
|
||||
EnvMutate(ctx context.Context, originalKey, resolvedKey, originalValue, currentValue string) (newValue string, stop bool, err error)
|
||||
}
|
||||
|
||||
var _ Mutator = (MutatorFunc)(nil)
|
||||
|
||||
// MutatorFunc implements the [Mutator] and provides a quick way to create an
|
||||
// anonymous function.
|
||||
type MutatorFunc func(ctx context.Context, originalKey, resolvedKey, originalValue, currentValue string) (newValue string, stop bool, err error)
|
||||
|
||||
// EnvMutate implements [Mutator].
|
||||
func (m MutatorFunc) EnvMutate(ctx context.Context, originalKey, resolvedKey, originalValue, currentValue string) (newValue string, stop bool, err error) {
|
||||
return m(ctx, originalKey, resolvedKey, originalValue, currentValue)
|
||||
}
|
||||
|
||||
// LegacyMutatorFunc is a helper that eases the transition from the previous
|
||||
// MutatorFunc signature. It wraps the previous-style mutator function and
|
||||
// returns a new one. Since the former mutator function had less data, this is
|
||||
// inherently lossy.
|
||||
//
|
||||
// Deprecated: Use [MutatorFunc] instead.
|
||||
func LegacyMutatorFunc(fn func(ctx context.Context, key, value string) (string, error)) MutatorFunc {
|
||||
return func(ctx context.Context, originalKey, resolvedKey, originalValue, currentValue string) (newValue string, stop bool, err error) {
|
||||
v, err := fn(ctx, originalKey, currentValue)
|
||||
return v, true, err
|
||||
}
|
||||
}
|
||||
4
vendor/modules.txt
generated
vendored
4
vendor/modules.txt
generated
vendored
@@ -801,8 +801,8 @@ github.com/segmentio/backo-go
|
||||
# github.com/sergi/go-diff v1.3.1
|
||||
## explicit; go 1.12
|
||||
github.com/sergi/go-diff/diffmatchpatch
|
||||
# github.com/sethvargo/go-envconfig v0.9.0
|
||||
## explicit; go 1.17
|
||||
# github.com/sethvargo/go-envconfig v1.0.0
|
||||
## explicit; go 1.21
|
||||
github.com/sethvargo/go-envconfig
|
||||
# github.com/sirupsen/logrus v1.9.2
|
||||
## explicit; go 1.13
|
||||
|
||||
Reference in New Issue
Block a user