⚠️ Work in progress!
A simple multi-layered config loader for Go.
Made for smaller projects. No external dependencies.
go get -u github.com/kdevo/config
// Define your configuration struct:
type Config struct {
RepoOwner string
RepoName string
URL string
HTTPTimeout time.Duration
}
// A config is complete when it can be validated.
// By optionally implementing the Config interface's Validate, we can detect errors.
func (c Config) Validate() error {
var errors config.Errors
if !strings.HasPrefix(c.URL, "http") {
// Capture errors on a per-field basis by naming the errors accordingly.
// This way, the config loader knows which fields are valid.
errors.Add(config.Err("URL", c.URL, "must be a valid URL (starting with http)"))
}
if c.HTTPTimeout < 1*time.Second {
errors.Add(config.Err("HTTPTimeout", c.HTTPTimeout, "must be >= 1s"))
}
return errors.AsError() // returns nil if we did not add any error
}
// Config providers follow below signature and return a config and eventually an error.
// There is nothing that prevents the config from being a provider for itself:
func (c Config) Config() (Config, error) {
return c, c.Validate() // makes some things easier later on
}
// Name the config provider accordingly to make it easily identifiable later on.
func (c Config) Name() string {
return "Static"
}
func main() {
// get config from 'JSON' first, fallback to 'Static' defaults otherwise.
// we can have any layer of chained config providers by using the builder funcs:
loader := config.From(provider.JSON("config.json")).
WithDefaults(Config{
RepoOwner: "kdevo",
RepoName: "config",
}) // works because we've implemented the config provider interface above
err := loader.Resolve() // calls Validate
if err != nil {
// examine errors by providers to find out the cause:
fmt.Printf("providers errors: %v\n", loader.ProviderErrors())
}
fmt.Printf("got config: %v\n", cfg)
}
Please also take a look at the config loader test.
- Earlier config providers take precedence.
- Config structs must be marshable.
- Empty/ommitted fields are skipped.
- Fields with errors are skipped (see below).
- Returning
config.Errors
with the specified field names will skip the field. - Field names must follow rules of encoding/json.
- Returning regular errors will skip the entire provider (e.g. if the config is corrupt).
Tips:
- Skip fields that couldn't be provided by using
config.Errors
(e.g. when a value has an invalid format). - Use descriptive field names that are unlikely to change.
- Return a regular error if we can't provide anything (e.g. Unmarshal error for JSON provider).