Last active
June 28, 2016 21:53
-
-
Save blockloop/d49f21aaeb1a6c06958aef3d595fd024 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"bufio" | |
"os" | |
"reflect" | |
"strconv" | |
"strings" | |
log "github.com/Sirupsen/logrus" | |
"github.com/pkg/errors" | |
) | |
const ( | |
whiteSpaces = " \t" | |
) | |
// Config is a representation of the configuration for this application | |
type Config struct { | |
Port string `config:"PORT"` | |
Host string `config:"HOST"` | |
Debug bool `config:"DEBUG"` | |
TLSCert string `config:"TLSCERT"` | |
TLSKey string `config:"TLSKEY"` | |
DBConStr string `config:"DBCONSTR"` | |
LogstashHost string `config:"LOGSTASHHOST"` | |
DBMaxIdle int `config:"DBMAXIDLE"` | |
DBMaxOpen int `config:"DBMAXOPEN"` | |
} | |
func parseConfig(filePath string) (*Config, error) { | |
// start with Environment variables because they are lowest precedence | |
config := &Config{} | |
for _, kv := range os.Environ() { | |
for i, c := range kv { | |
if c == '=' { | |
if err := setConfig(config, kv[:i], kv[i+1:]); err != nil { | |
return nil, errors.Wrapf(err, "error with environment variable") | |
} | |
break | |
} | |
} | |
} | |
// read configuration file | |
file, err := os.Open(filePath) | |
if err != nil { | |
return nil, errors.Wrapf(err, "could not read configuration file") | |
} | |
defer file.Close() | |
scanner := bufio.NewScanner(file) | |
lineNo := 1 | |
for scanner.Scan() { | |
line := strings.TrimLeft(scanner.Text(), whiteSpaces) | |
// skip commented lines and lines with 0 length | |
if len(line) == 0 || strings.HasPrefix(line, "#") { | |
continue | |
} | |
lineOk := false | |
Line: | |
for i, c := range line { | |
if c == '=' { | |
lineOk = true | |
if err := setConfig(config, line[:i], line[i+1:]); err != nil { | |
return nil, errors.Wrapf(err, "error with configuration value") | |
} | |
break Line | |
} | |
} | |
// lineOk is set to indicate that the line was in the correct format (i.e. key=value) | |
if !lineOk { | |
return nil, errors.Errorf("Line %d isn't in the correct format. Expected 'KEY=value', Got '%s'", lineNo, line) | |
} | |
lineNo++ | |
} | |
if err := scanner.Err(); err != nil { | |
log.Fatal(err) | |
} | |
return config, nil | |
} | |
func setConfig(conf *Config, k string, v string) error { | |
val := reflect.ValueOf(conf).Elem() | |
for i := 0; i < val.NumField(); i++ { | |
valueField := val.Field(i) | |
typeField := val.Type().Field(i) | |
if k == typeField.Tag.Get("config") { | |
if !valueField.CanSet() { | |
return errors.Errorf("Can't set value of %s because CanSet() returned false. See https://golang.org/pkg/reflect/#Value.CanSet for details", typeField.Name) | |
} | |
// valueField.Set(v) | |
if valueField.Kind() == reflect.Int { | |
i, err := strconv.Atoi(v) | |
if err != nil { | |
return errors.Wrapf(err, "Expected int. Got '%s'", v) | |
} | |
x := int64(i) | |
if !valueField.OverflowInt(x) { | |
valueField.SetInt(x) | |
} else { | |
return errors.Errorf("Number would overflow struct. %s", k) | |
} | |
} else if valueField.Kind() == reflect.String { | |
valueField.SetString(v) | |
} else if valueField.Kind() == reflect.Bool { | |
b, err := strconv.ParseBool(v) | |
if err != nil { | |
return errors.Wrapf(err, "Expected bool. Got '%s'", v) | |
} | |
valueField.SetBool(b) | |
} else { | |
return errors.Errorf("Don't know how to set type '%s'", valueField.Kind()) | |
} | |
break | |
} | |
} | |
return nil | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment