2017-12-08 20:02:26 +07:00
|
|
|
package config
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
2017-12-11 16:14:53 +07:00
|
|
|
"flag"
|
|
|
|
"fmt"
|
2017-12-08 20:02:26 +07:00
|
|
|
"io/ioutil"
|
2017-12-11 16:14:53 +07:00
|
|
|
"os"
|
|
|
|
"path/filepath"
|
2017-12-08 20:02:26 +07:00
|
|
|
"reflect"
|
2017-12-11 16:14:53 +07:00
|
|
|
|
|
|
|
"github.com/eschao/config/cli"
|
|
|
|
"github.com/eschao/config/env"
|
|
|
|
"github.com/eschao/config/utils"
|
|
|
|
"gopkg.in/yaml.v2"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
DefaultJSONConfig = "config.json"
|
|
|
|
DefaultYamlConfig = "config.yaml"
|
|
|
|
DefaultPropConfig = "config.properties"
|
2017-12-08 20:02:26 +07:00
|
|
|
)
|
|
|
|
|
2017-12-11 16:14:53 +07:00
|
|
|
const (
|
|
|
|
JSONConfigType = "json"
|
|
|
|
YamlConfigType = "yaml"
|
|
|
|
PropConfigType = "properties"
|
|
|
|
)
|
|
|
|
|
|
|
|
func ParseDefault(i interface{}) error {
|
|
|
|
ptrRef := reflect.ValueOf(i)
|
|
|
|
|
|
|
|
if ptrRef.IsNil() || ptrRef.Kind() != reflect.Ptr {
|
|
|
|
return fmt.Errorf("Expect a structure pointer type instead of %s",
|
|
|
|
ptrRef.Kind().String())
|
|
|
|
}
|
|
|
|
|
|
|
|
valueOfStruct := ptrRef.Elem()
|
|
|
|
if valueOfStruct.Kind() != reflect.Struct {
|
|
|
|
return fmt.Errorf("Expect a structure pointer type instead of %s",
|
|
|
|
valueOfStruct.Kind().String())
|
|
|
|
}
|
|
|
|
|
|
|
|
return parseValue(valueOfStruct)
|
|
|
|
}
|
|
|
|
|
|
|
|
func parseValue(v reflect.Value) error {
|
|
|
|
typeOfStruct := v.Type()
|
|
|
|
var err error
|
|
|
|
for i := 0; i < v.NumField() && err == nil; i++ {
|
|
|
|
valueOfField := v.Field(i)
|
|
|
|
kindOfField := valueOfField.Kind()
|
|
|
|
structOfField := typeOfStruct.Field(i)
|
|
|
|
|
|
|
|
if kindOfField == reflect.Ptr {
|
|
|
|
if !valueOfField.IsNil() && valueOfField.CanSet() {
|
|
|
|
err = ParseDefault(valueOfField.Interface())
|
|
|
|
} else {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
} else if kindOfField == reflect.Struct {
|
|
|
|
err = parseValue(valueOfField)
|
|
|
|
}
|
|
|
|
|
|
|
|
defValue, ok := structOfField.Tag.Lookup("default")
|
|
|
|
if !ok {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
kind := valueOfField.Kind()
|
|
|
|
switch kind {
|
|
|
|
case reflect.Bool:
|
|
|
|
err = utils.SetValueWithBool(valueOfField, defValue)
|
|
|
|
case reflect.String:
|
|
|
|
valueOfField.SetString(defValue)
|
|
|
|
case reflect.Int8:
|
|
|
|
err = utils.SetValueWithIntX(valueOfField, defValue, 8)
|
|
|
|
case reflect.Int16:
|
|
|
|
err = utils.SetValueWithIntX(valueOfField, defValue, 16)
|
|
|
|
case reflect.Int, reflect.Int32:
|
|
|
|
err = utils.SetValueWithIntX(valueOfField, defValue, 32)
|
|
|
|
case reflect.Int64:
|
|
|
|
err = utils.SetValueWithIntX(valueOfField, defValue, 64)
|
|
|
|
case reflect.Uint8:
|
|
|
|
err = utils.SetValueWithUintX(valueOfField, defValue, 8)
|
|
|
|
case reflect.Uint16:
|
|
|
|
err = utils.SetValueWithUintX(valueOfField, defValue, 16)
|
|
|
|
case reflect.Uint, reflect.Uint32:
|
|
|
|
err = utils.SetValueWithUintX(valueOfField, defValue, 32)
|
|
|
|
case reflect.Uint64:
|
|
|
|
err = utils.SetValueWithUintX(valueOfField, defValue, 64)
|
|
|
|
case reflect.Float32:
|
|
|
|
err = utils.SetValueWithFloatX(valueOfField, defValue, 32)
|
|
|
|
case reflect.Float64:
|
|
|
|
err = utils.SetValueWithFloatX(valueOfField, defValue, 64)
|
|
|
|
case reflect.Slice:
|
|
|
|
sp, ok := structOfField.Tag.Lookup("separator")
|
|
|
|
if !ok {
|
|
|
|
sp = ":"
|
|
|
|
}
|
|
|
|
err = utils.SetValueWithSlice(valueOfField, defValue, sp)
|
|
|
|
|
|
|
|
default:
|
|
|
|
return fmt.Errorf("Can't support type: %s", kind.String())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return err
|
2017-12-08 20:02:26 +07:00
|
|
|
}
|
|
|
|
|
2017-12-11 16:14:53 +07:00
|
|
|
func ParseEnv(i interface{}) error {
|
|
|
|
return env.ParseWith(i, "")
|
2017-12-08 20:02:26 +07:00
|
|
|
}
|
|
|
|
|
2017-12-11 16:14:53 +07:00
|
|
|
func ParseCli(i interface{}) error {
|
|
|
|
cli := cli.New(os.Args[0])
|
|
|
|
if err := cli.Init(i); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err := cli.Parse(os.Args[1:]); err != nil {
|
|
|
|
return err
|
2017-12-08 20:02:26 +07:00
|
|
|
}
|
2017-12-11 16:14:53 +07:00
|
|
|
return nil
|
|
|
|
}
|
2017-12-08 20:02:26 +07:00
|
|
|
|
2017-12-11 16:14:53 +07:00
|
|
|
func ParseConfig(i interface{}, configFlag string) error {
|
|
|
|
configFile := flag.String(configFlag, "", "Specifiy configuration file")
|
|
|
|
return ParseConfigFile(i, *configFile)
|
2017-12-08 20:02:26 +07:00
|
|
|
}
|
|
|
|
|
2017-12-11 16:14:53 +07:00
|
|
|
func ParseConfigFile(i interface{}, configFile string) error {
|
|
|
|
var err error
|
|
|
|
if configFile == "" {
|
|
|
|
configFile, err = getDefaultConfigFile()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
configType, err := getConfigFileType(configFile)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
switch configType {
|
|
|
|
case JSONConfigType:
|
|
|
|
return parseJSON(i, configFile)
|
|
|
|
case YamlConfigType:
|
|
|
|
return parseYaml(i, configFile)
|
|
|
|
case PropConfigType:
|
|
|
|
return parseProp(i, configFile)
|
|
|
|
default:
|
|
|
|
return fmt.Errorf("Can't support config file: %s", configFile)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func parseJSON(i interface{}, jsonFile string) error {
|
2017-12-08 20:02:26 +07:00
|
|
|
raw, err := ioutil.ReadFile(jsonFile)
|
|
|
|
if err != nil {
|
2017-12-11 16:14:53 +07:00
|
|
|
return fmt.Errorf("Can't open json config file. %s", err.Error())
|
2017-12-08 20:02:26 +07:00
|
|
|
}
|
|
|
|
|
2017-12-11 16:14:53 +07:00
|
|
|
return json.Unmarshal(raw, i)
|
|
|
|
}
|
|
|
|
|
|
|
|
func parseYaml(i interface{}, yamlFile string) error {
|
|
|
|
raw, err := ioutil.ReadFile(yamlFile)
|
2017-12-08 20:02:26 +07:00
|
|
|
if err != nil {
|
2017-12-11 16:14:53 +07:00
|
|
|
return fmt.Errorf("Can't open yaml config file. %s", err.Error())
|
2017-12-08 20:02:26 +07:00
|
|
|
}
|
|
|
|
|
2017-12-11 16:14:53 +07:00
|
|
|
return yaml.Unmarshal(raw, i)
|
|
|
|
}
|
2017-12-08 20:02:26 +07:00
|
|
|
|
2017-12-11 16:14:53 +07:00
|
|
|
func parseProp(i interface{}, propFile string) error {
|
|
|
|
return fmt.Errorf("Properties config has not implemented!")
|
|
|
|
}
|
|
|
|
|
|
|
|
func getDefaultConfigFile() (string, error) {
|
|
|
|
exe, err := os.Executable()
|
|
|
|
if err != nil {
|
|
|
|
return "", fmt.Errorf("Can't find default config file. %s", err.Error())
|
|
|
|
}
|
|
|
|
|
|
|
|
path := filepath.Dir(exe) + string(filepath.Separator)
|
|
|
|
|
|
|
|
// check json config
|
|
|
|
jsonConfig := path + DefaultJSONConfig
|
|
|
|
if _, err := os.Stat(jsonConfig); err == nil {
|
|
|
|
return jsonConfig, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// check yaml config
|
|
|
|
yamlConfig := path + DefaultYamlConfig
|
|
|
|
if _, err := os.Stat(yamlConfig); err == nil {
|
|
|
|
return yamlConfig, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// check prop config
|
|
|
|
propConfig := path + DefaultPropConfig
|
|
|
|
if _, err := os.Stat(propConfig); err == nil {
|
|
|
|
return propConfig, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return "", fmt.Errorf("No default config file found in path: %s", path)
|
|
|
|
}
|
|
|
|
|
|
|
|
func getConfigFileType(configFile string) (string, error) {
|
|
|
|
ext := filepath.Ext(configFile)
|
|
|
|
if ext == ".json" {
|
|
|
|
return JSONConfigType, nil
|
|
|
|
} else if ext == ".yaml" || ext == ".yml" {
|
|
|
|
return YamlConfigType, nil
|
|
|
|
} else if ext == ".properties" || ext == ".prop" {
|
|
|
|
return PropConfigType, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return "", fmt.Errorf("Can't support file type: %s", configFile)
|
2017-12-08 20:02:26 +07:00
|
|
|
}
|