130 lines
3.3 KiB
Go
130 lines
3.3 KiB
Go
package multiconfig
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"sort"
|
|
"strings"
|
|
|
|
"github.com/fatih/camelcase"
|
|
"github.com/fatih/structs"
|
|
)
|
|
|
|
// EnvironmentLoader satisifies the loader interface. It loads the
|
|
// configuration from the environment variables in the form of
|
|
// STRUCTNAME_FIELDNAME.
|
|
type EnvironmentLoader struct {
|
|
// Prefix prepends given string to every environment variable
|
|
// {STRUCTNAME}_FIELDNAME will be {PREFIX}_FIELDNAME
|
|
Prefix string
|
|
|
|
// CamelCase adds a separator for field names in camelcase form. A
|
|
// fieldname of "AccessKey" would generate a environment name of
|
|
// "STRUCTNAME_ACCESSKEY". If CamelCase is enabled, the environment name
|
|
// will be generated in the form of "STRUCTNAME_ACCESS_KEY"
|
|
CamelCase bool
|
|
}
|
|
|
|
func (e *EnvironmentLoader) getPrefix(s *structs.Struct) string {
|
|
if e.Prefix != "" {
|
|
return e.Prefix
|
|
}
|
|
|
|
return s.Name()
|
|
}
|
|
|
|
// Load loads the source into the config defined by struct s
|
|
func (e *EnvironmentLoader) Load(s interface{}) error {
|
|
strct := structs.New(s)
|
|
strctMap := strct.Map()
|
|
prefix := e.getPrefix(strct)
|
|
|
|
for key, val := range strctMap {
|
|
field := strct.Field(key)
|
|
|
|
if err := e.processField(prefix, field, key, val); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// processField gets leading name for the env variable and combines the current
|
|
// field's name and generates environment variable names recursively
|
|
func (e *EnvironmentLoader) processField(prefix string, field *structs.Field, name string, strctMap interface{}) error {
|
|
fieldName := e.generateFieldName(prefix, name)
|
|
|
|
switch strctMap.(type) {
|
|
case map[string]interface{}:
|
|
for key, val := range strctMap.(map[string]interface{}) {
|
|
field := field.Field(key)
|
|
|
|
if err := e.processField(fieldName, field, key, val); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
default:
|
|
v := os.Getenv(fieldName)
|
|
if v == "" {
|
|
return nil
|
|
}
|
|
|
|
if err := fieldSet(field, v); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// PrintEnvs prints the generated environment variables to the std out.
|
|
func (e *EnvironmentLoader) PrintEnvs(s interface{}) {
|
|
strct := structs.New(s)
|
|
strctMap := strct.Map()
|
|
prefix := e.getPrefix(strct)
|
|
|
|
keys := make([]string, 0, len(strctMap))
|
|
for key := range strctMap {
|
|
keys = append(keys, key)
|
|
}
|
|
sort.Strings(keys)
|
|
|
|
for _, key := range keys {
|
|
field := strct.Field(key)
|
|
e.printField(prefix, field, key, strctMap[key])
|
|
}
|
|
}
|
|
|
|
// printField prints the field of the config struct for the flag.Usage
|
|
func (e *EnvironmentLoader) printField(prefix string, field *structs.Field, name string, strctMap interface{}) {
|
|
fieldName := e.generateFieldName(prefix, name)
|
|
|
|
switch strctMap.(type) {
|
|
case map[string]interface{}:
|
|
smap := strctMap.(map[string]interface{})
|
|
keys := make([]string, 0, len(smap))
|
|
for key := range smap {
|
|
keys = append(keys, key)
|
|
}
|
|
sort.Strings(keys)
|
|
for _, key := range keys {
|
|
field := field.Field(key)
|
|
e.printField(fieldName, field, key, smap[key])
|
|
}
|
|
default:
|
|
fmt.Println(" ", fieldName)
|
|
}
|
|
}
|
|
|
|
// generateFieldName generates the field name combined with the prefix and the
|
|
// struct's field name
|
|
func (e *EnvironmentLoader) generateFieldName(prefix string, name string) string {
|
|
fieldName := strings.ToUpper(name)
|
|
if e.CamelCase {
|
|
fieldName = strings.ToUpper(strings.Join(camelcase.Split(name), "_"))
|
|
}
|
|
|
|
return strings.ToUpper(prefix) + "_" + fieldName
|
|
}
|