package config import ( "os" "strings" "fmt" "reflect" "errors" "strconv" ) var ConfigFetchMethod = os.Environ func Build(prefix string, configStruct interface{}) (err error) { defer func() { if e := recover(); e != nil { switch r := e.(type) { case string: err = errors.New(r) case error: err = r default: err = errors.New("Undefined panic") } } }() assertIsPtr(configStruct) fieldNames := getConfigStructFieldNames(configStruct) config := loadConfig(prefix) structRef := reflect.ValueOf(configStruct) for _, fieldName := range fieldNames { value, ok := config[fieldName] if !ok { // no such field loaded, avoid overwriting default continue } field := reflect.Indirect(structRef).FieldByName(fieldName) typeRef := field.Type() ret, err := tryParseString(value, typeRef.Kind()) if err != nil { return err } field.Set(reflect.ValueOf(ret).Convert(typeRef)) } return nil } func tryParseString(what string, toType reflect.Kind) (interface{}, error) { switch toType { case reflect.String: return what, nil case reflect.Int: i, err := strconv.Atoi(what) if err != nil { return nil, err } return i, nil case reflect.Bool: b, err := strconv.ParseBool(what) if err != nil { return nil, err } return b, nil default: return nil, fmt.Errorf("Failed to parse value %v", what) } } func assertIsPtr(what interface{}) { r := reflect.ValueOf(what) if r.Kind() != reflect.Ptr { panic(fmt.Errorf("Supplied value is not a pointer to a struct")) } } func getConfigStructFieldNames(configStruct interface{}) []string { configFields := []string{} val := reflect.ValueOf(configStruct).Elem() for i := 0; i < val.NumField(); i++ { configFields = append(configFields, val.Type().Field(i).Name) } return configFields } func loadConfig(prefix string) map[string]string { prefix = fmt.Sprintf("%s_", prefix) config := map[string]string{} for _, env := range ConfigFetchMethod() { parts := strings.SplitN(env, "=", 2) key, value := parts[0], parts[1] if strings.HasPrefix(key, prefix) { key = strings.TrimPrefix(key, prefix) config[key] = value } } return config } // CheckKeysExist returns an error if not all keys are present in config map func CheckKeysExist(config map[string]string, keys ...string) error { for _, key := range keys { if _, ok := config[key]; ok { return fmt.Errorf("Config key '%s' is not set", key) } } return nil }