Skip to content

Repository files navigation

ecp

Environment config parser

If you run your application in a container and deploy it via a docker-compose file, then you may need this tool for parsing configuration easily instead of mounting an external config file. You can simply set some environments and then ecp will help you fill the configs. Or, you can COPY a "default" config file to the image, and change some variables by overwriting the keys via environments.

The environment config keys can be auto generated or set by the yaml or env tag.

The only thing you should do is importing this package, and Parse your config.

Go Report CardGoGoDoclicense

Usage Example

package main
import (
"fmt""os""github.com/wrfly/ecp"
)
typeConfstruct {
LogLevelstring`default:"debug"`Portint`env:"PORT"`
}
funcmain() {
config:=&Conf{}
iferr:=ecp.Parse(config); err!=nil {
panic(err)
}
fmt.Printf("default log level: [ %s ]\n", config.LogLevel)
fmt.Println()
// set some envenvs:=map[string]string{
"LOGLEVEL": "info",
"PORT": "1234",
}
fork, v:=rangeenvs {
fmt.Printf("export %s=%s\n", k, v)
os.Setenv(k, v)
}
fmt.Println()
// then parse configuration from environmentsiferr:=ecp.Parse(config); err!=nil {
panic(err)
}
fmt.Printf("new log level: [ %s ], port: [ %d ]\n",
config.LogLevel, config.Port)
fmt.Println()
// and list all the env keysfor_, k:=rangeecp.List(config) {
fmt.Println(k)
}
}

Outputs:

default log level: [ debug ]
export LOGLEVEL=info
export PORT=1234
new log level: [ info ], port: [ 1234 ]
LOGLEVEL=debug
PORT=

Parse needs a pointer to a struct, ecp.Parse(config) on a plain struct returns an error instead of quietly filling in nothing.

Keys

The key of a field is built from the name of the struct it lives in and the name of the field itself, upper cased and joined with _. A yaml or json tag replaces the field name, and an env tag replaces the whole key:

typeConfstruct {
LogLevelstring// LOGLEVELRedisstruct {
Hoststring`yaml:"host"`// REDIS_HOSTPortint`env:"REDIS_PORT"`// REDIS_PORT
} `yaml:"redis"`Secretstring`yaml:"-"`// ignored
}

Pass a prefix to namespace them: ecp.Parse(&config, "APP") reads APP_LOGLEVEL and APP_REDIS_HOST. A key coming from an env tag is taken as is, so REDIS_PORT stays REDIS_PORT. The same prefix goes to List and to the Get helpers.

Values

Parse sets a field when the environment key exists, otherwise it falls back to the default tag. A default never overwrites a value the caller already set, an environment value always does.

Supported types are string, bool, all sized int/uint types, float32, float64, time.Duration, slices of those, and named types built on top of them. Anything else (a map, an array, ...) is reported as an error rather than silently skipped.

  • slices are separated by a space by default, change it with e := ecp.New(); e.Advance.SplitChar = ",". The default separator collapses repeats, so a b is two elements; a separator you choose is taken literally, empty elements and all
  • durations accept everything time.ParseDuration does, plus Xd for X days: 10s, 5m, 6d
  • integers also accept 1e3 and 1,000 notation. Slice elements do not: there 1,2 is far more likely to be the wrong separator than the number 12, so it is reported instead of quietly parsed
  • pointers (*int, *time.Duration, ...) only get their default when they are nil, which makes "unset" and "set to the zero value" distinguishable
  • pointers to a struct are optional sections: they are walked into like a plain struct and only allocated when one of their fields is actually set, whether from the environment or from a default tag. A section nothing was said about stays nil

An environment variable set to an empty value is treated as unset, so a field keeps its default.

Advanced

ecp.New() returns a parser whose behaviour can be changed:

e:=ecp.New()
e.BuildKey=func(structure, fieldstring, tag reflect.StructTag) string { ... }
e.LookupValue=func(keystring) (string, bool) { ... }
e.Advance.SplitChar=","e.Advance.SetValue=func(tag reflect.StructTag, field reflect.Value, valstring) bool { ... }

LookupValue is what makes it possible to read from something other than the environment, and SetValue takes over the conversion of a field, returning true when it handled it.

About

environment config parser | fill up the struct through the way you like

Topics

Resources

Stars

2 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages