Pre 1.0 version
This commit is contained in:
parent
50ac8e4572
commit
fca9c22d48
1
.gitignore
vendored
1
.gitignore
vendored
@ -3,6 +3,7 @@
|
||||
*.a
|
||||
*.so
|
||||
*.swp
|
||||
main
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
|
23
cli/cmd.go
23
cli/cmd.go
@ -137,32 +137,27 @@ func (this *Command) Init(i interface{}) error {
|
||||
|
||||
func (this *Command) parseValue(v reflect.Value) error {
|
||||
typeOfStruct := v.Type()
|
||||
var err error
|
||||
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
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() {
|
||||
cmd := this.createCliFlagSet(structOfField.Tag)
|
||||
if err := cmd.Init(valueOfField.Interface()); err != nil {
|
||||
return err
|
||||
}
|
||||
cmd := this.createSubCommand(structOfField.Tag)
|
||||
err = cmd.Init(valueOfField.Interface())
|
||||
}
|
||||
} else if kindOfField == reflect.Struct {
|
||||
cmd := this.createCliFlagSet(structOfField.Tag)
|
||||
if err := cmd.parseValue(valueOfField); err != nil {
|
||||
return err
|
||||
}
|
||||
cmd := this.createSubCommand(structOfField.Tag)
|
||||
err = cmd.parseValue(valueOfField)
|
||||
} else {
|
||||
if err := this.addFlag(valueOfField, structOfField); err != nil {
|
||||
return err
|
||||
}
|
||||
err = this.addFlag(valueOfField, structOfField)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
return err
|
||||
}
|
||||
|
||||
func (this *Command) addFlag(v reflect.Value, f reflect.StructField) error {
|
||||
@ -207,7 +202,7 @@ func (this *Command) addFlag(v reflect.Value, f reflect.StructField) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (this *Command) createCliFlagSet(tag reflect.StructTag) *Command {
|
||||
func (this *Command) createSubCommand(tag reflect.StructTag) *Command {
|
||||
cmdTag, ok := tag.Lookup("cmd")
|
||||
if !ok || cmdTag == "" {
|
||||
return this
|
||||
|
286
cli/cmd_test.go
286
cli/cmd_test.go
@ -5,59 +5,13 @@ import (
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/eschao/config/test"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
type dbConfig struct {
|
||||
Host string `cmd:"dbHost database server hostname"`
|
||||
Port int `cmd:"dbPort database server port"`
|
||||
User string `cmd:"dbUser database username"`
|
||||
Password string `cmd:"dbPassword database user password"`
|
||||
Log logConfig `cmd:"log database log configuration"`
|
||||
}
|
||||
|
||||
type loginConfig struct {
|
||||
User string `cmd:"user login username"`
|
||||
Password string `cmd:"password login password"`
|
||||
}
|
||||
|
||||
type logConfig struct {
|
||||
Path string `cmd:"path log path"`
|
||||
Level string `cmd:"level log level {debug|warning|error}"`
|
||||
}
|
||||
|
||||
type serviceConfig struct {
|
||||
Host string `cmd:"hostname service hostname"`
|
||||
Port int `cmd:"port service port"`
|
||||
DBConfig dbConfig `cmd:"database database configuration"`
|
||||
Login *loginConfig `cmd:"login login user and password"`
|
||||
Log logConfig `cmd:"log service log configuration"`
|
||||
}
|
||||
|
||||
type typesConfig struct {
|
||||
BoolValue bool `cmd:"bool boolean value"`
|
||||
StrValue string `cmd:"str string value"`
|
||||
Int8Value int8 `cmd:"int8 int8 value"`
|
||||
Int16Value int16 `cmd:"int16 int16 value"`
|
||||
IntValue int `cmd:"int int value"`
|
||||
Int32Value int32 `cmd:"int32 int32 value"`
|
||||
Int64Value int64 `cmd:"int64 int64 value"`
|
||||
Uint8Value uint8 `cmd:"uint8 uint8 value"`
|
||||
Uint16Value uint16 `cmd:"uint16 uint16 value"`
|
||||
UintValue uint `cmd:"uint uint value"`
|
||||
Uint32Value uint32 `cmd:"uint32 uint32 value"`
|
||||
Uint64Value uint64 `cmd:"uint64 uint64 value"`
|
||||
Float32Value float32 `cmd:"float32 float32 value"`
|
||||
Float64Value float64 `cmd:"float64 float64 value"`
|
||||
}
|
||||
|
||||
type defValueConfig struct {
|
||||
BoolValue bool `cmd:"bool boolean value" default:"true"`
|
||||
}
|
||||
|
||||
func TestServiceCommand(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
serviceConfig := serviceConfig{}
|
||||
serviceConfig := test.ServiceConfig{}
|
||||
cmd := New("Service")
|
||||
err := cmd.Init(&serviceConfig)
|
||||
if err != nil {
|
||||
@ -66,80 +20,65 @@ func TestServiceCommand(t *testing.T) {
|
||||
|
||||
// assert service cmd
|
||||
assert.NotNil(cmd.FlagSet)
|
||||
assert.NotNil(cmd.FlagSet.Lookup("hostname"),
|
||||
"service cmd should have {hostname} parameter")
|
||||
assert.NotNil(cmd.FlagSet.Lookup("port"),
|
||||
"service cmd should have {port} parameter")
|
||||
assert.Equal(2, len(cmd.SubCommands),
|
||||
"service cmd should have 2 sub cmds")
|
||||
assert.Nil(cmd.SubCommands["login"],
|
||||
"service cmd shouldn't have {login} sub cmd")
|
||||
assert.NotNil(cmd.FlagSet.Lookup("hostname"))
|
||||
assert.NotNil(cmd.FlagSet.Lookup("port"))
|
||||
assert.Equal(2, len(cmd.SubCommands))
|
||||
assert.Nil(cmd.SubCommands["login"])
|
||||
|
||||
// assert database sub cmd
|
||||
dbCmd := cmd.SubCommands["database"]
|
||||
assert.NotNil(dbCmd, "service cmd should have {database} sub cmd")
|
||||
assert.NotNil(dbCmd.FlagSet.Lookup("dbHost"),
|
||||
"database cmd should have {dbHost} parameter")
|
||||
assert.NotNil(dbCmd.FlagSet.Lookup("dbPort"),
|
||||
"database cmd should have {dbPort} parameter")
|
||||
assert.NotNil(dbCmd.FlagSet.Lookup("dbUser"),
|
||||
"database cmd should have {dbUser} parameter")
|
||||
assert.NotNil(dbCmd.FlagSet.Lookup("dbPassword"),
|
||||
"database cmd should have {dbPassword} parameter")
|
||||
assert.NotNil(dbCmd.FlagSet.Lookup("dbHost"))
|
||||
assert.NotNil(dbCmd.FlagSet.Lookup("dbPort"))
|
||||
assert.NotNil(dbCmd.FlagSet.Lookup("dbUser"))
|
||||
assert.NotNil(dbCmd.FlagSet.Lookup("dbPassword"))
|
||||
|
||||
// assert database log sub cmd
|
||||
dbLogCmd := dbCmd.SubCommands["log"]
|
||||
assert.NotNil(dbCmd, "database cmd should have {log} sub cmd")
|
||||
assert.NotNil(dbLogCmd.FlagSet.Lookup("path"),
|
||||
"database log cmd should have {path} parameter")
|
||||
assert.NotNil(dbLogCmd.FlagSet.Lookup("level"),
|
||||
"database log cmd should have {level} parameter")
|
||||
assert.Equal(0, len(dbLogCmd.SubCommands),
|
||||
"database log cmd shouldn't have sub cmd")
|
||||
assert.NotNil(dbLogCmd.FlagSet.Lookup("path"))
|
||||
assert.NotNil(dbLogCmd.FlagSet.Lookup("level"))
|
||||
assert.Equal(0, len(dbLogCmd.SubCommands))
|
||||
|
||||
// assert log cmd
|
||||
logCmd := cmd.SubCommands["log"]
|
||||
assert.NotNil(logCmd, "service cmd should have {log} sub cmd")
|
||||
assert.NotNil(logCmd.FlagSet.Lookup("path"),
|
||||
"log cmd should have {path} parameter")
|
||||
assert.NotNil(logCmd.FlagSet.Lookup("level"),
|
||||
"log cmd should have {level} parameter")
|
||||
assert.NotNil(logCmd.FlagSet.Lookup("path"))
|
||||
assert.NotNil(logCmd.FlagSet.Lookup("level"))
|
||||
}
|
||||
|
||||
func TestLoginSubCommand(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
serviceConfig := serviceConfig{Login: &loginConfig{}}
|
||||
serviceConfig := test.ServiceConfig{Login: &test.LoginConfig{}}
|
||||
cmd := New("Service")
|
||||
assert.NoError(cmd.Init(&serviceConfig), "Can't init service command")
|
||||
assert.NoError(cmd.Init(&serviceConfig))
|
||||
|
||||
// assert login sub command
|
||||
loginCmd := cmd.SubCommands["login"]
|
||||
assert.NotNil(loginCmd, "service cmd should have {login} sub cmd")
|
||||
assert.NotNil(loginCmd.FlagSet.Lookup("user"),
|
||||
"login cmd should have {user} parameter")
|
||||
assert.NotNil(loginCmd.FlagSet.Lookup("password"),
|
||||
"login cmd should have {password} parameter")
|
||||
assert.NotNil(loginCmd.FlagSet.Lookup("user"))
|
||||
assert.NotNil(loginCmd.FlagSet.Lookup("password"))
|
||||
}
|
||||
|
||||
func TestLoginCommandWithValues(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
loginConfig := loginConfig{}
|
||||
loginConfig := test.LoginConfig{}
|
||||
cmd := New("Login")
|
||||
assert.NoError(cmd.Init(&loginConfig), "Can't init login command")
|
||||
|
||||
username := "test-user"
|
||||
password := "test-passwd"
|
||||
args := []string{"-user", username, "--password", password}
|
||||
assert.NoError(cmd.Parse(args), "Can't parse login command")
|
||||
assert.Equal(username, loginConfig.User, "Failed to parse login command")
|
||||
assert.Equal(password, loginConfig.Password, "Failed to parse login command")
|
||||
assert.NoError(cmd.Parse(args))
|
||||
assert.Equal(username, loginConfig.User)
|
||||
assert.Equal(password, loginConfig.Password)
|
||||
}
|
||||
|
||||
func TestServiceCommandWithValues(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
serviceConfig := serviceConfig{Login: &loginConfig{}}
|
||||
serviceConfig := test.ServiceConfig{Login: &test.LoginConfig{}}
|
||||
cmd := New("Service")
|
||||
assert.NoError(cmd.Init(&serviceConfig), "Can't init service command")
|
||||
assert.NoError(cmd.Init(&serviceConfig))
|
||||
|
||||
serviceHost := "service-hostname"
|
||||
servicePort := 8080
|
||||
@ -159,155 +98,112 @@ func TestServiceCommandWithValues(t *testing.T) {
|
||||
serviceArgs := []string{"--hostname", serviceHost, "--port",
|
||||
strconv.Itoa(servicePort), "log", "-path", serviceLogPath, "-level",
|
||||
serviceLogLevel}
|
||||
assert.NoError(cmd.Parse(serviceArgs), "Can't parse service command")
|
||||
assert.Equal(serviceHost, serviceConfig.Host,
|
||||
"Service hostname is not equal")
|
||||
assert.Equal(servicePort, serviceConfig.Port,
|
||||
"Service port is not equal")
|
||||
assert.Equal(serviceLogPath, serviceConfig.Log.Path,
|
||||
"Service log path is not equal")
|
||||
assert.Equal(serviceLogLevel, serviceConfig.Log.Level,
|
||||
"Service log level is not equal")
|
||||
assert.NoError(cmd.Parse(serviceArgs))
|
||||
assert.Equal(serviceHost, serviceConfig.Host)
|
||||
assert.Equal(servicePort, serviceConfig.Port)
|
||||
assert.Equal(serviceLogPath, serviceConfig.Log.Path)
|
||||
assert.Equal(serviceLogLevel, serviceConfig.Log.Level)
|
||||
|
||||
dbCmdArgs := []string{"database", "-dbHost", dbHost, "-dbPort",
|
||||
strconv.Itoa(dbPort), "-dbUser", dbUser, "-dbPassword", dbPassword}
|
||||
assert.NoError(cmd.Parse(dbCmdArgs), "Can't parse service database command")
|
||||
assert.Equal(dbHost, serviceConfig.DBConfig.Host,
|
||||
"Database hostname is not equal")
|
||||
assert.Equal(dbPort, serviceConfig.DBConfig.Port,
|
||||
"Database port is not equal")
|
||||
assert.Equal(dbUser, serviceConfig.DBConfig.User,
|
||||
"Database username is not equal")
|
||||
assert.Equal(dbPassword, serviceConfig.DBConfig.Password,
|
||||
"Database password is not equal")
|
||||
assert.NoError(cmd.Parse(dbCmdArgs))
|
||||
assert.Equal(dbHost, serviceConfig.DBConfig.Host)
|
||||
assert.Equal(dbPort, serviceConfig.DBConfig.Port)
|
||||
assert.Equal(dbUser, serviceConfig.DBConfig.User)
|
||||
assert.Equal(dbPassword, serviceConfig.DBConfig.Password)
|
||||
|
||||
loginCmdArgs := []string{"login", "--user", loginUser, "-password",
|
||||
loginPassword}
|
||||
assert.NoError(cmd.Parse(loginCmdArgs), "Can't parse service login command")
|
||||
assert.Equal(loginUser, serviceConfig.Login.User,
|
||||
"Login username is not equal")
|
||||
assert.Equal(loginPassword, serviceConfig.Login.Password,
|
||||
"Login password is not equal")
|
||||
assert.NoError(cmd.Parse(loginCmdArgs))
|
||||
assert.Equal(loginUser, serviceConfig.Login.User)
|
||||
assert.Equal(loginPassword, serviceConfig.Login.Password)
|
||||
|
||||
dbLogCmdArgs := []string{"database", "log", "-path", dbLogPath, "-level",
|
||||
dbLogLevel}
|
||||
assert.NoError(cmd.Parse(dbLogCmdArgs), "Can't parse database log command")
|
||||
assert.Equal(dbLogPath, serviceConfig.DBConfig.Log.Path,
|
||||
"Database log path is not equal")
|
||||
assert.Equal(dbLogLevel, serviceConfig.DBConfig.Log.Level,
|
||||
"Database log level is not equal")
|
||||
assert.NoError(cmd.Parse(dbLogCmdArgs))
|
||||
assert.Equal(dbLogPath, serviceConfig.DBConfig.Log.Path)
|
||||
assert.Equal(dbLogLevel, serviceConfig.DBConfig.Log.Level)
|
||||
}
|
||||
|
||||
func TestVariousTypeCommand(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
typesConfig := typesConfig{}
|
||||
cmd := NewWith("Types", flag.ContinueOnError, nil)
|
||||
typesConfig := test.TypesConfig{}
|
||||
cmd := NewWith("Types", flag.ContinueOnError, func(cmd *Command) func() {
|
||||
return func() {
|
||||
}
|
||||
})
|
||||
assert.NoError(cmd.Init(&typesConfig))
|
||||
|
||||
// bool value
|
||||
assert.NoError(cmd.Parse([]string{"-bool=true"}),
|
||||
"Can't parse bool value command")
|
||||
assert.Equal(true, typesConfig.BoolValue, "Bool value is not true")
|
||||
assert.NoError(cmd.Parse([]string{"-bool"}),
|
||||
"Can't parse bool value command")
|
||||
assert.Equal(true, typesConfig.BoolValue, "Bool value is not false")
|
||||
assert.Error(cmd.Parse([]string{"-bool=xxx"}),
|
||||
"Parsing string as bool should have an error")
|
||||
assert.NoError(cmd.Parse([]string{"-bool=true"}))
|
||||
assert.Equal(true, typesConfig.BoolValue)
|
||||
assert.NoError(cmd.Parse([]string{"-bool"}))
|
||||
assert.Equal(true, typesConfig.BoolValue)
|
||||
assert.Error(cmd.Parse([]string{"-bool=xxx"}))
|
||||
|
||||
// string value
|
||||
assert.NoError(cmd.Parse([]string{"-str=xxx"}),
|
||||
"Can't parse string value command")
|
||||
assert.Equal("xxx", typesConfig.StrValue, "String value it not equal")
|
||||
assert.NoError(cmd.Parse([]string{"-str", "yyy"}),
|
||||
"Can't parse string value command")
|
||||
assert.Equal("yyy", typesConfig.StrValue, "String value is not equal")
|
||||
assert.NoError(cmd.Parse([]string{"-str=xxx"}))
|
||||
assert.Equal("xxx", typesConfig.StrValue)
|
||||
assert.NoError(cmd.Parse([]string{"-str", "yyy"}))
|
||||
assert.Equal("yyy", typesConfig.StrValue)
|
||||
|
||||
// int8 value
|
||||
assert.NoError(cmd.Parse([]string{"-int8=100"}),
|
||||
"Can't parse int8 value command")
|
||||
assert.Equal(int8(100), typesConfig.Int8Value, "Int8 value is not equal")
|
||||
assert.Error(cmd.Parse([]string{"-int8=xxx"}),
|
||||
"Parsing string as int8 should have an error")
|
||||
assert.NoError(cmd.Parse([]string{"-int8=100"}))
|
||||
assert.Equal(int8(100), typesConfig.Int8Value)
|
||||
assert.Error(cmd.Parse([]string{"-int8=xxx"}))
|
||||
|
||||
// int16 value
|
||||
assert.NoError(cmd.Parse([]string{"-int16=200"}),
|
||||
"Can't parse int16 value command")
|
||||
assert.Equal(int16(200), typesConfig.Int16Value, "Int16 value is not equal")
|
||||
assert.Error(cmd.Parse([]string{"-int16=xxx"}),
|
||||
"Parsing string as int16 should have an error")
|
||||
assert.NoError(cmd.Parse([]string{"-int16=200"}))
|
||||
assert.Equal(int16(200), typesConfig.Int16Value)
|
||||
assert.Error(cmd.Parse([]string{"-int16=xxx"}))
|
||||
|
||||
// int value
|
||||
assert.NoError(cmd.Parse([]string{"-int=300"}),
|
||||
"Can't parse int value command")
|
||||
assert.Equal(int(300), typesConfig.IntValue, "Int value is not equal")
|
||||
assert.Error(cmd.Parse([]string{"-int=xxx"}),
|
||||
"Parsing string as int should have an error")
|
||||
assert.NoError(cmd.Parse([]string{"-int=300"}))
|
||||
assert.Equal(int(300), typesConfig.IntValue)
|
||||
assert.Error(cmd.Parse([]string{"-int=xxx"}))
|
||||
|
||||
// int32 value
|
||||
assert.NoError(cmd.Parse([]string{"-int32=400"}),
|
||||
"Can't parse int32 value command")
|
||||
assert.Equal(int32(400), typesConfig.Int32Value, "Int32 value is not equal")
|
||||
assert.Error(cmd.Parse([]string{"-int32=xxx"}),
|
||||
"Parsing string as int32 should have an error")
|
||||
assert.NoError(cmd.Parse([]string{"-int32=400"}))
|
||||
assert.Equal(int32(400), typesConfig.Int32Value)
|
||||
assert.Error(cmd.Parse([]string{"-int32=xxx"}))
|
||||
|
||||
// int64 value
|
||||
assert.NoError(cmd.Parse([]string{"-int64=500"}),
|
||||
"Can't parse int64 value command")
|
||||
assert.Equal(int64(500), typesConfig.Int64Value, "Int64 value is not equal")
|
||||
assert.Error(cmd.Parse([]string{"-int64=xxx"}),
|
||||
"Parsing string as int64 should have an error")
|
||||
assert.NoError(cmd.Parse([]string{"-int64=500"}))
|
||||
assert.Equal(int64(500), typesConfig.Int64Value)
|
||||
assert.Error(cmd.Parse([]string{"-int64=xxx"}))
|
||||
|
||||
// uint8 value
|
||||
assert.NoError(cmd.Parse([]string{"-uint8=10"}),
|
||||
"Can't parse uint8 value command")
|
||||
assert.Equal(uint8(10), typesConfig.Uint8Value, "Uint8 value is not equal")
|
||||
assert.Error(cmd.Parse([]string{"-uint8=-10"}),
|
||||
"Parsing string as uint8 should have an error")
|
||||
assert.NoError(cmd.Parse([]string{"-uint8=10"}))
|
||||
assert.Equal(uint8(10), typesConfig.Uint8Value)
|
||||
assert.Error(cmd.Parse([]string{"-uint8=-10"}))
|
||||
|
||||
// uint16 value
|
||||
assert.NoError(cmd.Parse([]string{"-uint16=1000"}),
|
||||
"Can't parse uint16 value command")
|
||||
assert.Equal(uint16(1000), typesConfig.Uint16Value,
|
||||
"Uint16 value is not equal")
|
||||
assert.Error(cmd.Parse([]string{"-uint16=xxx"}),
|
||||
"Parsing string as uint16 should have an error")
|
||||
assert.NoError(cmd.Parse([]string{"-uint16=1000"}))
|
||||
assert.Equal(uint16(1000), typesConfig.Uint16Value)
|
||||
assert.Error(cmd.Parse([]string{"-uint16=xxx"}))
|
||||
|
||||
// uint value
|
||||
assert.NoError(cmd.Parse([]string{"-uint=2000"}),
|
||||
"Can't parse uint value command")
|
||||
assert.Equal(uint(2000), typesConfig.UintValue, "Uint value is not equal")
|
||||
assert.Error(cmd.Parse([]string{"-uint=xxx"}),
|
||||
"Parsing string as uint should have an error")
|
||||
assert.NoError(cmd.Parse([]string{"-uint=2000"}))
|
||||
assert.Equal(uint(2000), typesConfig.UintValue)
|
||||
assert.Error(cmd.Parse([]string{"-uint=xxx"}))
|
||||
|
||||
// uint32 value
|
||||
assert.NoError(cmd.Parse([]string{"-uint32=3000"}),
|
||||
"Can't parse uint32 value command")
|
||||
assert.Equal(uint32(3000), typesConfig.Uint32Value,
|
||||
"Uint32 value is not equal")
|
||||
assert.Error(cmd.Parse([]string{"-uint32=xxx"}),
|
||||
"Parsing string as uint32 should have an error")
|
||||
assert.NoError(cmd.Parse([]string{"-uint32=3000"}))
|
||||
assert.Equal(uint32(3000), typesConfig.Uint32Value)
|
||||
assert.Error(cmd.Parse([]string{"-uint32=xxx"}))
|
||||
|
||||
// uint64 value
|
||||
assert.NoError(cmd.Parse([]string{"-uint64=4000"}),
|
||||
"Can't parse uint64 value command")
|
||||
assert.Equal(uint64(4000), typesConfig.Uint64Value,
|
||||
"Uint64 value is not equal")
|
||||
assert.Error(cmd.Parse([]string{"-uint64=xxx"}),
|
||||
"Parsing string as uint64 should have an error")
|
||||
assert.NoError(cmd.Parse([]string{"-uint64=4000"}))
|
||||
assert.Equal(uint64(4000), typesConfig.Uint64Value)
|
||||
assert.Error(cmd.Parse([]string{"-uint64=xxx"}))
|
||||
|
||||
// float32 value
|
||||
assert.NoError(cmd.Parse([]string{"-float32=1.234"}),
|
||||
"Can't parse float32 value command")
|
||||
assert.Equal(float32(1.234), typesConfig.Float32Value,
|
||||
"Float32 value is not equal")
|
||||
assert.Error(cmd.Parse([]string{"-float32=xxx"}),
|
||||
"Parsing string as float32 should have an error")
|
||||
assert.NoError(cmd.Parse([]string{"-float32=1.234"}))
|
||||
assert.Equal(float32(1.234), typesConfig.Float32Value)
|
||||
assert.Error(cmd.Parse([]string{"-float32=xxx"}))
|
||||
|
||||
// float64 value
|
||||
assert.NoError(cmd.Parse([]string{"-float64=2.345"}),
|
||||
"Can't parse float64 value command")
|
||||
assert.Equal(float64(2.345), typesConfig.Float64Value,
|
||||
"Float64 value is not equal")
|
||||
assert.Error(cmd.Parse([]string{"-float64=xxx"}),
|
||||
"Parsing string as float64 should have an error")
|
||||
assert.NoError(cmd.Parse([]string{"-float64=2.345"}))
|
||||
assert.Equal(float64(2.345), typesConfig.Float64Value)
|
||||
assert.Error(cmd.Parse([]string{"-float64=xxx"}))
|
||||
}
|
||||
|
226
config.go
226
config.go
@ -2,46 +2,220 @@ package config
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
|
||||
"github.com/eschao/config/cli"
|
||||
"github.com/eschao/config/env"
|
||||
"github.com/eschao/config/utils"
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
type Field struct {
|
||||
JsonName string
|
||||
YamlName string
|
||||
PropName string
|
||||
EnvName string
|
||||
CliName string
|
||||
Value reflect.Value
|
||||
DefaultValue string
|
||||
Separator string
|
||||
}
|
||||
const (
|
||||
DefaultJSONConfig = "config.json"
|
||||
DefaultYamlConfig = "config.yaml"
|
||||
DefaultPropConfig = "config.properties"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Fields []Field
|
||||
}
|
||||
const (
|
||||
JSONConfigType = "json"
|
||||
YamlConfigType = "yaml"
|
||||
PropConfigType = "properties"
|
||||
)
|
||||
|
||||
func (this *Config) Init() *Config {
|
||||
if this.Fields == nil {
|
||||
this.Fields = []Field{}
|
||||
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())
|
||||
}
|
||||
|
||||
return this
|
||||
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 (this *Config) ParseJSON(jsonFile string, data interface{}) error {
|
||||
raw, err := ioutil.ReadFile(jsonFile)
|
||||
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
|
||||
}
|
||||
|
||||
func ParseEnv(i interface{}) error {
|
||||
return env.ParseWith(i, "")
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func ParseConfig(i interface{}, configFlag string) error {
|
||||
configFile := flag.String(configFlag, "", "Specifiy configuration file")
|
||||
return ParseConfigFile(i, *configFile)
|
||||
}
|
||||
|
||||
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 errors.New("Can't open json file. Err: " + err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
err = json.Unmarshal(raw, data)
|
||||
if err != nil {
|
||||
return errors.New("Failed unmarshal json. Err: " + err.Error())
|
||||
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)
|
||||
}
|
||||
|
||||
//fmt.Printf("Data: %v", *data.(*interface{}))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseJSON(i interface{}, jsonFile string) error {
|
||||
raw, err := ioutil.ReadFile(jsonFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Can't open json config file. %s", err.Error())
|
||||
}
|
||||
|
||||
return json.Unmarshal(raw, i)
|
||||
}
|
||||
|
||||
func parseYaml(i interface{}, yamlFile string) error {
|
||||
raw, err := ioutil.ReadFile(yamlFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Can't open yaml config file. %s", err.Error())
|
||||
}
|
||||
|
||||
return yaml.Unmarshal(raw, i)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
106
config_test.go
106
config_test.go
@ -1,27 +1,101 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/eschao/config/test"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
type TestConfig struct {
|
||||
Name string `json:"name" default:"test-name"`
|
||||
Path string `json:"path" default:"./"`
|
||||
const (
|
||||
LOGIN_USER = "test-login-user"
|
||||
LOGIN_PASSWORD = "test-login-passwd"
|
||||
SERVICE_HOST = "test-service-host"
|
||||
SERVICE_PORT = 8080
|
||||
SERVICE_LOG_PATH = "/var/log/service"
|
||||
SERVICE_LOG_LEVEL = "debug"
|
||||
DB_HOST = "test-db-host"
|
||||
DB_PORT = 9090
|
||||
DB_USER = "test-db-user"
|
||||
DB_PASSWORD = "test-db-password"
|
||||
DB_LOG_PATH = "/var/log/db"
|
||||
DB_LOG_LEVEL = "error"
|
||||
)
|
||||
|
||||
func TestDefaultValueConfig(t *testing.T) {
|
||||
conf := test.DefValueConfig{}
|
||||
assert := assert.New(t)
|
||||
assert.NoError(ParseDefault(&conf))
|
||||
|
||||
assert.Equal(true, conf.BoolValue)
|
||||
assert.Equal(123, conf.IntValue)
|
||||
assert.Equal(float64(123.4567), conf.Float64Value)
|
||||
assert.Equal("default-string", conf.StrValue)
|
||||
assert.Equal(3, len(conf.SliceValue))
|
||||
assert.Equal("xx", conf.SliceValue[0])
|
||||
assert.Equal("yy", conf.SliceValue[1])
|
||||
assert.Equal("zz", conf.SliceValue[2])
|
||||
assert.Equal("", conf.NoDefValue)
|
||||
}
|
||||
|
||||
func TestJSONConfig(t *testing.T) {
|
||||
config := Config{}
|
||||
myConfig := TestConfig{}
|
||||
err := config.Init().ParseJSON("test.json", &myConfig)
|
||||
if err != nil {
|
||||
t.Errorf("JSON config test failed. ", err.Error())
|
||||
}
|
||||
func TestEnvConfig(t *testing.T) {
|
||||
dbLogPrefix := "LOG_"
|
||||
|
||||
if myConfig.Name != "jsonconfig" {
|
||||
t.Errorf("Name json value: %s != jsonconfig", myConfig.Name)
|
||||
}
|
||||
os.Setenv("HOST", DB_HOST)
|
||||
os.Setenv("PORT", strconv.Itoa(DB_PORT))
|
||||
os.Setenv("USER", DB_USER)
|
||||
os.Setenv("PASSWORD", DB_PASSWORD)
|
||||
os.Setenv(dbLogPrefix+"PATH", DB_LOG_PATH)
|
||||
os.Setenv(dbLogPrefix+"LEVEL", DB_LOG_LEVEL)
|
||||
|
||||
if myConfig.Path != "/var" {
|
||||
t.Errorf("Path json value: %s != /var", myConfig.Path)
|
||||
}
|
||||
defer os.Unsetenv("HOST")
|
||||
defer os.Unsetenv("PORT")
|
||||
defer os.Unsetenv("USER")
|
||||
defer os.Unsetenv("PASSWORD")
|
||||
defer os.Unsetenv(dbLogPrefix + "PATH")
|
||||
defer os.Unsetenv(dbLogPrefix + "LEVEL")
|
||||
|
||||
conf := test.DBConfig{}
|
||||
assert := assert.New(t)
|
||||
assert.NoError(ParseEnv(&conf))
|
||||
assert.Equal(DB_HOST, conf.Host)
|
||||
assert.Equal(DB_PORT, conf.Port)
|
||||
assert.Equal(DB_USER, conf.User)
|
||||
assert.Equal(DB_PASSWORD, conf.Password)
|
||||
assert.Equal(DB_LOG_PATH, conf.Log.Path)
|
||||
assert.Equal(DB_LOG_LEVEL, conf.Log.Level)
|
||||
}
|
||||
|
||||
func TestJSONConfigFile(t *testing.T) {
|
||||
_, curTestFile, _, _ := runtime.Caller(0)
|
||||
path := filepath.Dir(curTestFile)
|
||||
|
||||
conf := test.DBConfig{}
|
||||
assert := assert.New(t)
|
||||
assert.NoError(ParseConfigFile(&conf, path+"/test/config.json"))
|
||||
assert.Equal(DB_HOST, conf.Host)
|
||||
assert.Equal(DB_PORT, conf.Port)
|
||||
assert.Equal(DB_USER, conf.User)
|
||||
assert.Equal(DB_PASSWORD, conf.Password)
|
||||
assert.Equal(DB_LOG_PATH, conf.Log.Path)
|
||||
assert.Equal(DB_LOG_LEVEL, conf.Log.Level)
|
||||
}
|
||||
|
||||
func TestYamlConfigFile(t *testing.T) {
|
||||
_, curTestFile, _, _ := runtime.Caller(0)
|
||||
path := filepath.Dir(curTestFile)
|
||||
|
||||
conf := test.DBConfig{}
|
||||
assert := assert.New(t)
|
||||
assert.NoError(ParseConfigFile(&conf, path+"/test/config.yaml"))
|
||||
assert.Equal(DB_HOST, conf.Host)
|
||||
assert.Equal(DB_PORT, conf.Port)
|
||||
assert.Equal(DB_USER, conf.User)
|
||||
assert.Equal(DB_PASSWORD, conf.Password)
|
||||
assert.Equal(DB_LOG_PATH, conf.Log.Path)
|
||||
assert.Equal(DB_LOG_LEVEL, conf.Log.Level)
|
||||
}
|
||||
|
183
env/env.go
vendored
183
env/env.go
vendored
@ -4,11 +4,15 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/eschao/config/utils"
|
||||
)
|
||||
|
||||
func Unmarshal(i interface{}) error {
|
||||
func Parse(i interface{}) error {
|
||||
return ParseWith(i, "")
|
||||
}
|
||||
|
||||
func ParseWith(i interface{}, prefix string) error {
|
||||
ptrRef := reflect.ValueOf(i)
|
||||
|
||||
if ptrRef.IsNil() || ptrRef.Kind() != reflect.Ptr {
|
||||
@ -22,37 +26,37 @@ func Unmarshal(i interface{}) error {
|
||||
valueOfStruct.Kind().String())
|
||||
}
|
||||
|
||||
return unmarshal(valueOfStruct)
|
||||
return parseValue(valueOfStruct, prefix)
|
||||
}
|
||||
|
||||
func unmarshal(v reflect.Value) error {
|
||||
func parseValue(v reflect.Value, prefix string) error {
|
||||
typeOfStruct := v.Type()
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
var err error
|
||||
for i := 0; i < v.NumField() && err == nil; i++ {
|
||||
valueOfField := v.Field(i)
|
||||
kindOfField := valueOfField.Kind()
|
||||
structOfField := typeOfStruct.Field(i)
|
||||
//fmt.Printf("Name: %s, Type: %s\n", structOfField.Name, kindOfField.String())
|
||||
|
||||
// recursively unmarshal if value is ptr type
|
||||
if kindOfField == reflect.Ptr {
|
||||
if !valueOfField.IsNil() && valueOfField.CanSet() {
|
||||
Unmarshal(valueOfField.Interface())
|
||||
err = ParseWith(valueOfField.Interface(),
|
||||
prefix+structOfField.Tag.Get("env"))
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
} else if kindOfField == reflect.Struct {
|
||||
unmarshal(valueOfField)
|
||||
err = parseValue(valueOfField, prefix+structOfField.Tag.Get("env"))
|
||||
}
|
||||
|
||||
if err := setFieldValue(valueOfField, structOfField); err != nil {
|
||||
return err
|
||||
}
|
||||
err = setFieldValue(valueOfField, structOfField, prefix)
|
||||
}
|
||||
|
||||
return nil
|
||||
return err
|
||||
}
|
||||
|
||||
func getEnvValue(envName string, f reflect.StructField) (string, bool) {
|
||||
//fmt.Printf("Lookup ENV: %s\n", envName)
|
||||
envValue, ok := os.LookupEnv(envName)
|
||||
if !ok {
|
||||
envValue, ok = f.Tag.Lookup("default")
|
||||
@ -61,13 +65,13 @@ func getEnvValue(envName string, f reflect.StructField) (string, bool) {
|
||||
return envValue, ok
|
||||
}
|
||||
|
||||
func setFieldValue(v reflect.Value, f reflect.StructField) error {
|
||||
func setFieldValue(v reflect.Value, f reflect.StructField, prefix string) error {
|
||||
envName := f.Tag.Get("env")
|
||||
if envName == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
envValue, ok := getEnvValue(envName, f)
|
||||
envValue, ok := getEnvValue(prefix+envName, f)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
@ -76,168 +80,47 @@ func setFieldValue(v reflect.Value, f reflect.StructField) error {
|
||||
return fmt.Errorf("%s: can't be set", f.Name)
|
||||
}
|
||||
|
||||
var err error
|
||||
kind := v.Kind()
|
||||
name := f.Name
|
||||
switch kind {
|
||||
case reflect.Bool:
|
||||
return setFieldValueWithBool(name, v, envValue)
|
||||
|
||||
err = utils.SetValueWithBool(v, envValue)
|
||||
case reflect.String:
|
||||
v.SetString(envValue)
|
||||
return nil
|
||||
|
||||
case reflect.Int8:
|
||||
return setFieldValueWithIntX(name, v, envValue, 8)
|
||||
|
||||
err = utils.SetValueWithIntX(v, envValue, 8)
|
||||
case reflect.Int16:
|
||||
return setFieldValueWithIntX(name, v, envValue, 16)
|
||||
|
||||
err = utils.SetValueWithIntX(v, envValue, 16)
|
||||
case reflect.Int, reflect.Int32:
|
||||
return setFieldValueWithIntX(name, v, envValue, 32)
|
||||
|
||||
err = utils.SetValueWithIntX(v, envValue, 32)
|
||||
case reflect.Int64:
|
||||
return setFieldValueWithIntX(name, v, envValue, 64)
|
||||
|
||||
err = utils.SetValueWithIntX(v, envValue, 64)
|
||||
case reflect.Uint8:
|
||||
return setFieldValueWithUintX(name, v, envValue, 8)
|
||||
|
||||
err = utils.SetValueWithUintX(v, envValue, 8)
|
||||
case reflect.Uint16:
|
||||
return setFieldValueWithUintX(name, v, envValue, 16)
|
||||
|
||||
err = utils.SetValueWithUintX(v, envValue, 16)
|
||||
case reflect.Uint, reflect.Uint32:
|
||||
return setFieldValueWithUintX(name, v, envValue, 32)
|
||||
|
||||
err = utils.SetValueWithUintX(v, envValue, 32)
|
||||
case reflect.Uint64:
|
||||
return setFieldValueWithUintX(name, v, envValue, 64)
|
||||
|
||||
err = utils.SetValueWithUintX(v, envValue, 64)
|
||||
case reflect.Float32:
|
||||
return setFieldValueWithFloatX(name, v, envValue, 32)
|
||||
|
||||
err = utils.SetValueWithFloatX(v, envValue, 32)
|
||||
case reflect.Float64:
|
||||
return setFieldValueWithFloatX(name, v, envValue, 64)
|
||||
err = utils.SetValueWithFloatX(v, envValue, 64)
|
||||
|
||||
case reflect.Slice:
|
||||
sp, ok := f.Tag.Lookup("separator")
|
||||
if !ok {
|
||||
sp = ":"
|
||||
}
|
||||
return setFieldValueWithSlice(name, v, envValue, sp)
|
||||
err = utils.SetValueWithSlice(v, envValue, sp)
|
||||
|
||||
default:
|
||||
return fmt.Errorf("Can't support type: %s", kind.String())
|
||||
}
|
||||
}
|
||||
|
||||
func setFieldValueWithBool(name string, v reflect.Value,
|
||||
envValue string) error {
|
||||
value, err := strconv.ParseBool(envValue)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: can't convert %s to bool value. %s", name, envValue,
|
||||
err.Error())
|
||||
return fmt.Errorf("%s: %s", f.Name, err.Error())
|
||||
}
|
||||
|
||||
v.SetBool(value)
|
||||
return nil
|
||||
}
|
||||
|
||||
func setFieldValueWithFloatX(name string, v reflect.Value, envValue string,
|
||||
bitSize int) error {
|
||||
value, err := strconv.ParseFloat(envValue, bitSize)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: can't convert %s to float%d value. %s", name,
|
||||
envValue, bitSize, err.Error())
|
||||
}
|
||||
|
||||
v.SetFloat(value)
|
||||
return nil
|
||||
}
|
||||
|
||||
func setFieldValueWithIntX(name string, v reflect.Value, envValue string,
|
||||
bitSize int) error {
|
||||
value, err := strconv.ParseInt(envValue, 10, bitSize)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: can't convert %s to int%d value. %s", name,
|
||||
envValue, bitSize, err.Error())
|
||||
}
|
||||
|
||||
v.SetInt(value)
|
||||
return nil
|
||||
}
|
||||
|
||||
func setFieldValueWithUintX(name string, v reflect.Value, envValue string,
|
||||
bitSize int) error {
|
||||
value, err := strconv.ParseUint(envValue, 10, bitSize)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: can't convert %s to uint%d value. %s", name,
|
||||
envValue, bitSize, err.Error())
|
||||
}
|
||||
|
||||
v.SetUint(value)
|
||||
return nil
|
||||
}
|
||||
|
||||
func setFieldValueWithSlice(name string, v reflect.Value, envValue string,
|
||||
separator string) error {
|
||||
data := strings.Split(envValue, separator)
|
||||
size := len(data)
|
||||
if size > 0 {
|
||||
slice := reflect.MakeSlice(v.Type(), size, size)
|
||||
for i := 0; i < size; i++ {
|
||||
ele := slice.Index(i)
|
||||
kind := ele.Kind()
|
||||
switch kind {
|
||||
case reflect.Bool:
|
||||
if err := setFieldValueWithBool(name, ele, data[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
case reflect.String:
|
||||
ele.SetString(data[i])
|
||||
case reflect.Uint8:
|
||||
if err := setFieldValueWithUintX(name, ele, data[i], 8); err != nil {
|
||||
return err
|
||||
}
|
||||
case reflect.Uint16:
|
||||
if err := setFieldValueWithUintX(name, ele, data[i], 16); err != nil {
|
||||
return err
|
||||
}
|
||||
case reflect.Uint, reflect.Uint32:
|
||||
if err := setFieldValueWithUintX(name, ele, data[i], 32); err != nil {
|
||||
return err
|
||||
}
|
||||
case reflect.Uint64:
|
||||
if err := setFieldValueWithUintX(name, ele, data[i], 64); err != nil {
|
||||
return err
|
||||
}
|
||||
case reflect.Int8:
|
||||
if err := setFieldValueWithIntX(name, ele, data[i], 8); err != nil {
|
||||
return err
|
||||
}
|
||||
case reflect.Int16:
|
||||
if err := setFieldValueWithIntX(name, ele, data[i], 16); err != nil {
|
||||
return err
|
||||
}
|
||||
case reflect.Int, reflect.Int32:
|
||||
if err := setFieldValueWithIntX(name, ele, data[i], 32); err != nil {
|
||||
return err
|
||||
}
|
||||
case reflect.Int64:
|
||||
if err := setFieldValueWithIntX(name, ele, data[i], 64); err != nil {
|
||||
return err
|
||||
}
|
||||
case reflect.Float32:
|
||||
if err := setFieldValueWithFloatX(name, ele, data[i], 32); err != nil {
|
||||
return err
|
||||
}
|
||||
case reflect.Float64:
|
||||
if err := setFieldValueWithFloatX(name, ele, data[i], 64); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("%s: can't support type: %s", name, kind.String())
|
||||
}
|
||||
}
|
||||
v.Set(slice)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
393
env/env_test.go
vendored
393
env/env_test.go
vendored
@ -1,194 +1,259 @@
|
||||
package env
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type EnvConfig1 struct {
|
||||
Hostname string `env:"CONFIG_TEST_HOSTNAME" default:"localhost"`
|
||||
Port int `env:"CONFIG_TEST_PORT"`
|
||||
User string `env:"CONFIG_TEST_USER"`
|
||||
Password string `env:"CONFIG_TEST_PASSWORD"`
|
||||
Path1 []string `env:"CONFIG_TEST_PATH1"`
|
||||
Path2 []string `env:"CONFIG_TEST_PATH2" separator:";"`
|
||||
Home string
|
||||
}
|
||||
"github.com/eschao/config/test"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
const (
|
||||
TEST_HOSTNAME = "test-hostname"
|
||||
TEST_PORT = 8080
|
||||
TEST_USER = "test-user"
|
||||
TEST_PASSWORD = "test-password"
|
||||
TEST_PATH1 = "/usr:/var:/bin"
|
||||
TEST_PATH2 = "/root;/home;/tmp"
|
||||
LOGIN_USER = "test-login-user"
|
||||
LOGIN_PASSWORD = "test-login-passwd"
|
||||
SERVICE_HOST = "test-service-host"
|
||||
SERVICE_PORT = 8080
|
||||
SERVICE_LOG_PATH = "/var/log/service"
|
||||
SERVICE_LOG_LEVEL = "debug"
|
||||
DB_HOST = "test-db-host"
|
||||
DB_PORT = 9090
|
||||
DB_USER = "test-db-user"
|
||||
DB_PASSWORD = "test-db-password"
|
||||
DB_LOG_PATH = "/var/log/db"
|
||||
DB_LOG_LEVEL = "error"
|
||||
)
|
||||
|
||||
func setEnvConfig1() {
|
||||
os.Setenv("CONFIG_TEST_HOSTNAME", TEST_HOSTNAME)
|
||||
os.Setenv("CONFIG_TEST_PORT", strconv.Itoa(TEST_PORT))
|
||||
os.Setenv("CONFIG_TEST_USER", TEST_USER)
|
||||
os.Setenv("CONFIG_TEST_PASSWORD", TEST_PASSWORD)
|
||||
os.Setenv("CONFIG_TEST_PATH1", TEST_PATH1)
|
||||
os.Setenv("CONFIG_TEST_PATH2", TEST_PATH2)
|
||||
func TestLoginConfigEnv(t *testing.T) {
|
||||
os.Setenv("USER", LOGIN_USER)
|
||||
os.Setenv("PASSWORD", LOGIN_PASSWORD)
|
||||
defer os.Unsetenv("USER")
|
||||
defer os.Unsetenv("PASSWORD")
|
||||
|
||||
assert := assert.New(t)
|
||||
loginConfig := test.LoginConfig{}
|
||||
assert.NoError(Parse(&loginConfig))
|
||||
|
||||
assert.Equal(LOGIN_USER, loginConfig.User)
|
||||
assert.Equal(LOGIN_PASSWORD, loginConfig.Password)
|
||||
}
|
||||
|
||||
func unsetEnvConfig1() {
|
||||
os.Unsetenv("CONFIG_TEST_HOSTNAME")
|
||||
os.Unsetenv("CONFIG_TEST_PORT")
|
||||
os.Unsetenv("CONFIG_TEST_USER")
|
||||
os.Unsetenv("CONFIG_TEST_PASSWORD")
|
||||
func TestLoginConfigEnvWithPrefix(t *testing.T) {
|
||||
os.Setenv("DB_USER", LOGIN_USER)
|
||||
os.Setenv("DB_PASSWORD", LOGIN_PASSWORD)
|
||||
defer os.Unsetenv("DB_USER")
|
||||
defer os.Unsetenv("DB_PASSWORD")
|
||||
|
||||
assert := assert.New(t)
|
||||
loginConfig := test.LoginConfig{}
|
||||
assert.NoError(ParseWith(&loginConfig, "DB_"))
|
||||
assert.Equal(LOGIN_USER, loginConfig.User)
|
||||
assert.Equal(LOGIN_PASSWORD, loginConfig.Password)
|
||||
}
|
||||
|
||||
func assertEqual(expected, actual []string) (bool, error) {
|
||||
if len(expected) != len(actual) {
|
||||
return false, fmt.Errorf("Expected length of array is %d, but actual is %d",
|
||||
len(expected), len(actual))
|
||||
}
|
||||
func TestServiceConfigEnv(t *testing.T) {
|
||||
servicePrefix := "CONFIG_TEST_SERVICE_"
|
||||
serviceLogPrefix := servicePrefix + "LOG_"
|
||||
dbPrefix := servicePrefix + "DB_"
|
||||
dbLogPrefix := dbPrefix + "LOG_"
|
||||
|
||||
for i := 0; i < len(expected); i++ {
|
||||
if expected[i] != actual[i] {
|
||||
return false, fmt.Errorf("Expected array[%d]=%s, but acutal array[%d]=%s",
|
||||
i, expected[i], i, actual[i])
|
||||
}
|
||||
}
|
||||
os.Setenv(servicePrefix+"HOST", SERVICE_HOST)
|
||||
os.Setenv(servicePrefix+"PORT", strconv.Itoa(SERVICE_PORT))
|
||||
os.Setenv(serviceLogPrefix+"PATH", SERVICE_LOG_PATH)
|
||||
os.Setenv(serviceLogPrefix+"LEVEL", SERVICE_LOG_LEVEL)
|
||||
os.Setenv(dbPrefix+"HOST", DB_HOST)
|
||||
os.Setenv(dbPrefix+"PORT", strconv.Itoa(DB_PORT))
|
||||
os.Setenv(dbPrefix+"USER", DB_USER)
|
||||
os.Setenv(dbPrefix+"PASSWORD", DB_PASSWORD)
|
||||
os.Setenv(dbLogPrefix+"PATH", DB_LOG_PATH)
|
||||
os.Setenv(dbLogPrefix+"LEVEL", DB_LOG_LEVEL)
|
||||
|
||||
return true, nil
|
||||
defer os.Unsetenv(servicePrefix + "HOST")
|
||||
defer os.Unsetenv(servicePrefix + "PORT")
|
||||
defer os.Unsetenv(serviceLogPrefix + "PATH")
|
||||
defer os.Unsetenv(serviceLogPrefix + "LEVEL")
|
||||
defer os.Unsetenv(dbPrefix + "HOST")
|
||||
defer os.Unsetenv(dbPrefix + "PORT")
|
||||
defer os.Unsetenv(dbPrefix + "USER")
|
||||
defer os.Unsetenv(dbPrefix + "PASSWORD")
|
||||
defer os.Unsetenv(dbLogPrefix + "PATH")
|
||||
defer os.Unsetenv(dbLogPrefix + "LEVEL")
|
||||
|
||||
assert := assert.New(t)
|
||||
serviceConfig := test.ServiceConfig{}
|
||||
assert.NoError(Parse(&serviceConfig))
|
||||
assert.Equal(SERVICE_HOST, serviceConfig.Host)
|
||||
assert.Equal(SERVICE_PORT, serviceConfig.Port)
|
||||
assert.Equal(SERVICE_LOG_PATH, serviceConfig.Log.Path)
|
||||
assert.Equal(SERVICE_LOG_LEVEL, serviceConfig.Log.Level)
|
||||
assert.Equal(DB_HOST, serviceConfig.DBConfig.Host)
|
||||
assert.Equal(DB_PORT, serviceConfig.DBConfig.Port)
|
||||
assert.Equal(DB_USER, serviceConfig.DBConfig.User)
|
||||
assert.Equal(DB_PASSWORD, serviceConfig.DBConfig.Password)
|
||||
assert.Equal(DB_LOG_PATH, serviceConfig.DBConfig.Log.Path)
|
||||
assert.Equal(DB_LOG_LEVEL, serviceConfig.DBConfig.Log.Level)
|
||||
}
|
||||
|
||||
func TestEnvConfig1(t *testing.T) {
|
||||
setEnvConfig1()
|
||||
defer unsetEnvConfig1()
|
||||
func TestServiceLoginConfigEnv(t *testing.T) {
|
||||
serviceLoginPrefix := "CONFIG_TEST_SERVICE_LOGIN_"
|
||||
os.Setenv(serviceLoginPrefix+"USER", LOGIN_USER)
|
||||
os.Setenv(serviceLoginPrefix+"PASSWORD", LOGIN_PASSWORD)
|
||||
defer os.Unsetenv(serviceLoginPrefix + "USER")
|
||||
defer os.Unsetenv(serviceLoginPrefix + "PASSWORD")
|
||||
|
||||
conf := EnvConfig1{}
|
||||
err := Unmarshal(&conf)
|
||||
if err != nil {
|
||||
t.Errorf("Can't unmarshal config1 from environemnt variables. %s",
|
||||
err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if conf.Hostname != TEST_HOSTNAME {
|
||||
t.Errorf("Expect Hostname: %s, but got: %s", TEST_HOSTNAME, conf.Hostname)
|
||||
}
|
||||
|
||||
if conf.Port != TEST_PORT {
|
||||
t.Errorf("Expect Port: %d, but got: %d", TEST_PORT, conf.Port)
|
||||
}
|
||||
|
||||
if conf.User != TEST_USER {
|
||||
t.Errorf("Expect User: %s, but got: %s", TEST_USER, conf.User)
|
||||
}
|
||||
|
||||
if conf.Password != TEST_PASSWORD {
|
||||
t.Errorf("Expect Password: %s, but got: %s", TEST_PASSWORD, conf.Password)
|
||||
}
|
||||
|
||||
if conf.Home != "" {
|
||||
t.Errorf("Expect Home is empty, but got: %s", conf.Home)
|
||||
}
|
||||
|
||||
expectedPath1 := strings.Split(TEST_PATH1, ":")
|
||||
if ok, err := assertEqual(expectedPath1, conf.Path1); !ok {
|
||||
t.Error(err.Error())
|
||||
}
|
||||
|
||||
expectedPath2 := strings.Split(TEST_PATH2, ";")
|
||||
if ok, err := assertEqual(expectedPath2, conf.Path2); !ok {
|
||||
t.Error(err.Error())
|
||||
}
|
||||
assert := assert.New(t)
|
||||
serviceConfig := test.ServiceConfig{Login: &test.LoginConfig{}}
|
||||
assert.NoError(Parse(&serviceConfig))
|
||||
assert.Equal(LOGIN_USER, serviceConfig.Login.User)
|
||||
assert.Equal(LOGIN_PASSWORD, serviceConfig.Login.Password)
|
||||
}
|
||||
|
||||
func TestEnvConfig1WithDefaultValue(t *testing.T) {
|
||||
os.Setenv("CONFIG_TEST_PORT", strconv.Itoa(TEST_PORT))
|
||||
os.Setenv("CONFIG_TEST_USER", TEST_USER)
|
||||
os.Setenv("CONFIG_TEST_PASSWORD", TEST_PASSWORD)
|
||||
defer unsetEnvConfig1()
|
||||
func TestTypesConfigEnv(t *testing.T) {
|
||||
typesPrefix := "CONFIG_TEST_"
|
||||
os.Setenv(typesPrefix+"BOOL", "true")
|
||||
os.Setenv(typesPrefix+"STR", "test-string")
|
||||
os.Setenv(typesPrefix+"INT8", "100")
|
||||
os.Setenv(typesPrefix+"INT16", "1000")
|
||||
os.Setenv(typesPrefix+"INT", "10000")
|
||||
os.Setenv(typesPrefix+"INT32", "100000")
|
||||
os.Setenv(typesPrefix+"INT64", "1000000")
|
||||
os.Setenv(typesPrefix+"UINT8", "200")
|
||||
os.Setenv(typesPrefix+"UINT16", "2000")
|
||||
os.Setenv(typesPrefix+"UINT", "20000")
|
||||
os.Setenv(typesPrefix+"UINT32", "200000")
|
||||
os.Setenv(typesPrefix+"UINT64", "2000000")
|
||||
os.Setenv(typesPrefix+"FLOAT32", "1.234")
|
||||
os.Setenv(typesPrefix+"FLOAT64", "2222.33333")
|
||||
|
||||
conf := EnvConfig1{}
|
||||
err := Unmarshal(&conf)
|
||||
if err != nil {
|
||||
t.Errorf("Can't unmarshal config1 from environemnt variables. %s",
|
||||
err.Error())
|
||||
return
|
||||
}
|
||||
defer os.Unsetenv(typesPrefix + "BOOL")
|
||||
defer os.Unsetenv(typesPrefix + "STR")
|
||||
defer os.Unsetenv(typesPrefix + "INT8")
|
||||
defer os.Unsetenv(typesPrefix + "INT16")
|
||||
defer os.Unsetenv(typesPrefix + "INT")
|
||||
defer os.Unsetenv(typesPrefix + "INT32")
|
||||
defer os.Unsetenv(typesPrefix + "INT64")
|
||||
defer os.Unsetenv(typesPrefix + "UINT8")
|
||||
defer os.Unsetenv(typesPrefix + "UINT16")
|
||||
defer os.Unsetenv(typesPrefix + "UINT")
|
||||
defer os.Unsetenv(typesPrefix + "UINT32")
|
||||
defer os.Unsetenv(typesPrefix + "UINT64")
|
||||
defer os.Unsetenv(typesPrefix + "FLOAT32")
|
||||
defer os.Unsetenv(typesPrefix + "FLOAT64")
|
||||
|
||||
if conf.Hostname != "localhost" {
|
||||
t.Errorf("Expect Hostname: localhost, bug got: %s", conf.Hostname)
|
||||
}
|
||||
|
||||
if conf.Port != TEST_PORT {
|
||||
t.Errorf("Expect Port: %d, but got: %d", TEST_PORT, conf.Port)
|
||||
}
|
||||
|
||||
if conf.User != TEST_USER {
|
||||
t.Errorf("Expect User: %s, but got: %s", TEST_USER, conf.User)
|
||||
}
|
||||
|
||||
if conf.Password != TEST_PASSWORD {
|
||||
t.Errorf("Expect Password: %s, but got: %s", TEST_PASSWORD, conf.Password)
|
||||
}
|
||||
|
||||
if conf.Home != "" {
|
||||
t.Errorf("Expect Home is empty, but got: %s", conf.Home)
|
||||
}
|
||||
assert := assert.New(t)
|
||||
typesConfig := test.TypesConfig{}
|
||||
assert.NoError(Parse(&typesConfig))
|
||||
assert.Equal(true, typesConfig.BoolValue)
|
||||
assert.Equal("test-string", typesConfig.StrValue)
|
||||
assert.Equal(int8(100), typesConfig.Int8Value)
|
||||
assert.Equal(int16(1000), typesConfig.Int16Value)
|
||||
assert.Equal(10000, typesConfig.IntValue)
|
||||
assert.Equal(int32(100000), typesConfig.Int32Value)
|
||||
assert.Equal(int64(1000000), typesConfig.Int64Value)
|
||||
assert.Equal(uint8(200), typesConfig.Uint8Value)
|
||||
assert.Equal(uint16(2000), typesConfig.Uint16Value)
|
||||
assert.Equal(uint(20000), typesConfig.UintValue)
|
||||
assert.Equal(uint32(200000), typesConfig.Uint32Value)
|
||||
assert.Equal(uint64(2000000), typesConfig.Uint64Value)
|
||||
assert.Equal(float32(1.234), typesConfig.Float32Value)
|
||||
assert.Equal(float64(2222.33333), typesConfig.Float64Value)
|
||||
}
|
||||
|
||||
type EnvConfig2 struct {
|
||||
Config1 EnvConfig1
|
||||
Server string `env:"CONFIG_ENV_TEST_SERVER"`
|
||||
func TestTypesConfigWithErrorEnv(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
typesConfig := test.TypesConfig{}
|
||||
typesPrefix := "CONFIG_TEST_"
|
||||
os.Setenv(typesPrefix+"BOOL", "xxx")
|
||||
assert.Error(Parse(&typesConfig))
|
||||
os.Unsetenv(typesPrefix + "BOOL")
|
||||
|
||||
os.Setenv(typesPrefix+"INT8", "xxx")
|
||||
assert.Error(Parse(&typesConfig))
|
||||
os.Unsetenv(typesPrefix + "INT8")
|
||||
|
||||
os.Setenv(typesPrefix+"INT16", "xxx")
|
||||
assert.Error(Parse(&typesConfig))
|
||||
os.Unsetenv(typesPrefix + "INT16")
|
||||
|
||||
os.Setenv(typesPrefix+"INT", "xxx")
|
||||
assert.Error(Parse(&typesConfig))
|
||||
os.Unsetenv(typesPrefix + "INT")
|
||||
|
||||
os.Setenv(typesPrefix+"INT32", "xxx")
|
||||
assert.Error(Parse(&typesConfig))
|
||||
os.Unsetenv(typesPrefix + "INT32")
|
||||
|
||||
os.Setenv(typesPrefix+"INT64", "xxx")
|
||||
assert.Error(Parse(&typesConfig))
|
||||
os.Unsetenv(typesPrefix + "INT64")
|
||||
|
||||
os.Setenv(typesPrefix+"UINT8", "xxx")
|
||||
assert.Error(Parse(&typesConfig))
|
||||
os.Unsetenv(typesPrefix + "UINT8")
|
||||
|
||||
os.Setenv(typesPrefix+"UINT16", "xxx")
|
||||
assert.Error(Parse(&typesConfig))
|
||||
os.Unsetenv(typesPrefix + "UINT16")
|
||||
|
||||
os.Setenv(typesPrefix+"UINT", "xxx")
|
||||
assert.Error(Parse(&typesConfig))
|
||||
os.Unsetenv(typesPrefix + "UINT")
|
||||
|
||||
os.Setenv(typesPrefix+"UINT32", "xxx")
|
||||
assert.Error(Parse(&typesConfig))
|
||||
os.Unsetenv(typesPrefix + "UINT32")
|
||||
|
||||
os.Setenv(typesPrefix+"UINT64", "xxx")
|
||||
assert.Error(Parse(&typesConfig))
|
||||
os.Unsetenv(typesPrefix + "UINT64")
|
||||
|
||||
os.Setenv(typesPrefix+"FLOAT32", "xxx")
|
||||
assert.Error(Parse(&typesConfig))
|
||||
os.Unsetenv(typesPrefix + "FLOAT32")
|
||||
|
||||
os.Setenv(typesPrefix+"FLOAT64", "xxx")
|
||||
assert.Error(Parse(&typesConfig))
|
||||
os.Unsetenv(typesPrefix + "FLOAT64")
|
||||
|
||||
defer os.Unsetenv(typesPrefix + "BOOL")
|
||||
defer os.Unsetenv(typesPrefix + "INT8")
|
||||
defer os.Unsetenv(typesPrefix + "INT16")
|
||||
defer os.Unsetenv(typesPrefix + "INT")
|
||||
defer os.Unsetenv(typesPrefix + "INT32")
|
||||
defer os.Unsetenv(typesPrefix + "INT64")
|
||||
defer os.Unsetenv(typesPrefix + "UINT8")
|
||||
defer os.Unsetenv(typesPrefix + "UINT16")
|
||||
defer os.Unsetenv(typesPrefix + "UINT")
|
||||
defer os.Unsetenv(typesPrefix + "UINT32")
|
||||
defer os.Unsetenv(typesPrefix + "UINT64")
|
||||
defer os.Unsetenv(typesPrefix + "FLOAT32")
|
||||
defer os.Unsetenv(typesPrefix + "FLOAT64")
|
||||
}
|
||||
|
||||
const (
|
||||
TEST_SERVER = "test-server"
|
||||
)
|
||||
func TestSlicesConfigEnv(t *testing.T) {
|
||||
prefix := "CONFIG_TEST_SLICES_"
|
||||
os.Setenv(prefix+"PATHS", "/var:/usr:/home")
|
||||
os.Setenv(prefix+"DEBUG", "/root;/log;/opt")
|
||||
os.Setenv(prefix+"VALUES", "1,2,4,5")
|
||||
|
||||
func setEnvConfig2() {
|
||||
setEnvConfig1()
|
||||
os.Setenv("CONFIG_ENV_TEST_SERVER", TEST_SERVER)
|
||||
}
|
||||
|
||||
func unsetEnvConfig2() {
|
||||
unsetEnvConfig1()
|
||||
os.Unsetenv("CONFIG_ENV_TEST_SERVER")
|
||||
}
|
||||
|
||||
func TestEnvConfig2(t *testing.T) {
|
||||
setEnvConfig2()
|
||||
defer unsetEnvConfig2()
|
||||
|
||||
conf := EnvConfig2{}
|
||||
err := Unmarshal(&conf)
|
||||
if err != nil {
|
||||
t.Errorf("Can't unmarshal config2 from environemnt variables. %s",
|
||||
err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if conf.Config1.Hostname != TEST_HOSTNAME {
|
||||
t.Errorf("Expect Hostname: %s, but got: %s", TEST_HOSTNAME, conf.Config1.Hostname)
|
||||
}
|
||||
|
||||
if conf.Config1.Port != TEST_PORT {
|
||||
t.Errorf("Expect Port: %d, but got: %d", TEST_PORT, conf.Config1.Port)
|
||||
}
|
||||
|
||||
if conf.Config1.User != TEST_USER {
|
||||
t.Errorf("Expect User: %s, but got: %s", TEST_USER, conf.Config1.User)
|
||||
}
|
||||
|
||||
if conf.Config1.Password != TEST_PASSWORD {
|
||||
t.Errorf("Expect Password: %s, but got: %s", TEST_PASSWORD, conf.Config1.Password)
|
||||
}
|
||||
|
||||
if conf.Config1.Home != "" {
|
||||
t.Errorf("Expect Home is empty, but got: %s", conf.Config1.Home)
|
||||
}
|
||||
|
||||
if conf.Server != TEST_SERVER {
|
||||
t.Errorf("Expect Server: %s, but got: %s", TEST_SERVER, conf.Server)
|
||||
}
|
||||
defer os.Unsetenv(prefix + "PATHS")
|
||||
defer os.Unsetenv(prefix + "DEBUG")
|
||||
defer os.Unsetenv(prefix + "VALUES")
|
||||
|
||||
assert := assert.New(t)
|
||||
conf := test.SlicesConfig{}
|
||||
assert.NoError(Parse(&conf))
|
||||
assert.Equal(3, len(conf.Paths))
|
||||
assert.Equal("/var", conf.Paths[0])
|
||||
assert.Equal("/usr", conf.Paths[1])
|
||||
assert.Equal("/home", conf.Paths[2])
|
||||
assert.Equal(3, len(conf.Debugs))
|
||||
assert.Equal("/root", conf.Debugs[0])
|
||||
assert.Equal("/log", conf.Debugs[1])
|
||||
assert.Equal("/opt", conf.Debugs[2])
|
||||
assert.Equal(4, len(conf.Values))
|
||||
assert.Equal(1, conf.Values[0])
|
||||
assert.Equal(2, conf.Values[1])
|
||||
assert.Equal(4, conf.Values[2])
|
||||
assert.Equal(5, conf.Values[3])
|
||||
}
|
||||
|
114
main.go
114
main.go
@ -1,114 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
//"github.com/eschao/Config/cli"
|
||||
)
|
||||
|
||||
type DBCli struct {
|
||||
Host string `cli:"hostname database server hostname"`
|
||||
Port string `cli:"port database server port"`
|
||||
User string `cli:"user database username"`
|
||||
Password string `cli:"password database user password"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
countCommand := flag.NewFlagSet("count", flag.ExitOnError)
|
||||
listCommand := flag.NewFlagSet("list", flag.ExitOnError)
|
||||
|
||||
// Count subcommand flag pointers
|
||||
// Adding a new choice for --metric of 'substring' and a new --substring flag
|
||||
countTextPtr := countCommand.String("text", "", "Text to parse. (Required)")
|
||||
countMetricPtr := countCommand.String("metric", "chars", "Metric {chars|words|lines|substring}. (Required)")
|
||||
countSubstringPtr := countCommand.String("substring", "", "The substring to be counted. Required for --metric=substring")
|
||||
countUniquePtr := countCommand.Bool("unique", false, "Measure unique values of a metric.")
|
||||
|
||||
// List subcommand flag pointers
|
||||
listTextPtr := listCommand.String("text", "", "Text to parse. (Required)")
|
||||
listMetricPtr := listCommand.String("metric", "chars", "Metric <chars|words|lines>. (Required)")
|
||||
listUniquePtr := listCommand.Bool("unique", false, "Measure unique values of a metric.")
|
||||
|
||||
// Verify that a subcommand has been provided
|
||||
// os.Arg[0] is the main command
|
||||
// os.Arg[1] will be the subcommand
|
||||
if len(os.Args) < 2 {
|
||||
//fmt.Println("list or count subcommand is required")
|
||||
flag.PrintDefaults()
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Switch on the subcommand
|
||||
// Parse the flags for appropriate FlagSet
|
||||
// FlagSet.Parse() requires a set of arguments to parse as input
|
||||
// os.Args[2:] will be all arguments starting after the subcommand at os.Args[1]
|
||||
switch os.Args[1] {
|
||||
case "list":
|
||||
listCommand.Parse(os.Args[2:])
|
||||
case "count":
|
||||
countCommand.Parse(os.Args[2:])
|
||||
default:
|
||||
flag.PrintDefaults()
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Check which subcommand was Parsed using the FlagSet.Parsed() function. Handle each case accordingly.
|
||||
// FlagSet.Parse() will evaluate to false if no flags were parsed (i.e. the user did not provide any flags)
|
||||
if listCommand.Parsed() {
|
||||
// Required Flags
|
||||
if *listTextPtr == "" {
|
||||
listCommand.PrintDefaults()
|
||||
os.Exit(1)
|
||||
}
|
||||
//Choice flag
|
||||
metricChoices := map[string]bool{"chars": true, "words": true, "lines": true}
|
||||
if _, validChoice := metricChoices[*listMetricPtr]; !validChoice {
|
||||
listCommand.PrintDefaults()
|
||||
os.Exit(1)
|
||||
}
|
||||
// Print
|
||||
fmt.Printf("textPtr: %s, metricPtr: %s, uniquePtr: %t\n", *listTextPtr, *listMetricPtr, *listUniquePtr)
|
||||
}
|
||||
|
||||
if countCommand.Parsed() {
|
||||
// Required Flags
|
||||
if *countTextPtr == "" {
|
||||
countCommand.PrintDefaults()
|
||||
os.Exit(1)
|
||||
}
|
||||
// If the metric flag is substring, the substring flag is required
|
||||
if *countMetricPtr == "substring" && *countSubstringPtr == "" {
|
||||
countCommand.PrintDefaults()
|
||||
os.Exit(1)
|
||||
}
|
||||
//If the metric flag is not substring, the substring flag must not be used
|
||||
if *countMetricPtr != "substring" && *countSubstringPtr != "" {
|
||||
fmt.Println("--substring may only be used with --metric=substring.")
|
||||
countCommand.PrintDefaults()
|
||||
os.Exit(1)
|
||||
}
|
||||
//Choice flag
|
||||
metricChoices := map[string]bool{"chars": true, "words": true, "lines": true, "substring": true}
|
||||
if _, validChoice := metricChoices[*listMetricPtr]; !validChoice {
|
||||
countCommand.PrintDefaults()
|
||||
os.Exit(1)
|
||||
}
|
||||
//Print
|
||||
fmt.Printf("textPtr: %s, metricPtr: %s, substringPtr: %v, uniquePtr: %t\n", *countTextPtr, *countMetricPtr, *countSubstringPtr, *countUniquePtr)
|
||||
}
|
||||
/*
|
||||
config := DBCli{}
|
||||
root := cli.CliFlag{}
|
||||
err := cli.Parse(&config, &root)
|
||||
|
||||
if err != nil {
|
||||
fmt.Printf("Can't parse cli. %s", err.Error())
|
||||
}
|
||||
flag.Parse()
|
||||
|
||||
fmt.Printf("Host=%s\n", config.Host)
|
||||
*/
|
||||
|
||||
}
|
10
test/config.json
Normal file
10
test/config.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"dbHost": "test-db-host",
|
||||
"dbPort": 9090,
|
||||
"dbUser": "test-db-user",
|
||||
"dbPassword": "test-db-password",
|
||||
"log": {
|
||||
"path": "/var/log/db",
|
||||
"level": "error"
|
||||
}
|
||||
}
|
8
test/config.yaml
Normal file
8
test/config.yaml
Normal file
@ -0,0 +1,8 @@
|
||||
dbHost: test-db-host
|
||||
dbPort: 9090
|
||||
dbUser: test-db-user
|
||||
dbPassword: test-db-password
|
||||
log:
|
||||
path: /var/log/db
|
||||
level: error
|
||||
|
59
test/data.go
Normal file
59
test/data.go
Normal file
@ -0,0 +1,59 @@
|
||||
package test
|
||||
|
||||
type DBConfig struct {
|
||||
Host string `json:"dbHost" yaml:"dbHost" env:"HOST" cmd:"dbHost database server hostname"`
|
||||
Port int `json:"dbPort" yaml:"dbPort" env:"PORT" cmd:"dbPort database server port"`
|
||||
User string `json:"dbUser" yaml:"dbUser" env:"USER" cmd:"dbUser database username"`
|
||||
Password string `json:"dbPassword" yaml:"dbPassword" env:"PASSWORD" cmd:"dbPassword database user password"`
|
||||
Log LogConfig `json:"log" yaml:"log" env:"LOG_" cmd:"log database log configuration"`
|
||||
}
|
||||
|
||||
type LoginConfig struct {
|
||||
User string `json:"user" yaml:"user" env:"USER" cmd:"user login username"`
|
||||
Password string `json:"password" yaml:"password" env:"PASSWORD" cmd:"password login password"`
|
||||
}
|
||||
|
||||
type LogConfig struct {
|
||||
Path string `json:"path" yaml:"path" env:"PATH" cmd:"path log path"`
|
||||
Level string `json:"level" yaml:"level" env:"LEVEL" cmd:"level log level {debug|warning|error}"`
|
||||
}
|
||||
|
||||
type ServiceConfig struct {
|
||||
Host string `env:"CONFIG_TEST_SERVICE_HOST" cmd:"hostname service hostname"`
|
||||
Port int `env:"CONFIG_TEST_SERVICE_PORT" cmd:"port service port"`
|
||||
DBConfig DBConfig `env:"CONFIG_TEST_SERVICE_DB_" cmd:"database database configuration"`
|
||||
Login *LoginConfig `env:"CONFIG_TEST_SERVICE_LOGIN_" cmd:"login login user and password"`
|
||||
Log LogConfig `env:"CONFIG_TEST_SERVICE_LOG_" cmd:"log service log configuration"`
|
||||
}
|
||||
|
||||
type TypesConfig struct {
|
||||
BoolValue bool `env:"CONFIG_TEST_BOOL" cmd:"bool boolean value"`
|
||||
StrValue string `env:"CONFIG_TEST_STR" cmd:"str string value"`
|
||||
Int8Value int8 `env:"CONFIG_TEST_INT8" cmd:"int8 int8 value"`
|
||||
Int16Value int16 `env:"CONFIG_TEST_INT16" cmd:"int16 int16 value"`
|
||||
IntValue int `env:"CONFIG_TEST_INT" cmd:"int int value"`
|
||||
Int32Value int32 `env:"CONFIG_TEST_INT32" cmd:"int32 int32 value"`
|
||||
Int64Value int64 `env:"CONFIG_TEST_INT64" cmd:"int64 int64 value"`
|
||||
Uint8Value uint8 `env:"CONFIG_TEST_UINT8" cmd:"uint8 uint8 value"`
|
||||
Uint16Value uint16 `env:"CONFIG_TEST_UINT16" cmd:"uint16 uint16 value"`
|
||||
UintValue uint `env:"CONFIG_TEST_UINT" cmd:"uint uint value"`
|
||||
Uint32Value uint32 `env:"CONFIG_TEST_UINT32" cmd:"uint32 uint32 value"`
|
||||
Uint64Value uint64 `env:"CONFIG_TEST_UINT64" cmd:"uint64 uint64 value"`
|
||||
Float32Value float32 `env:"CONFIG_TEST_FLOAT32" cmd:"float32 float32 value"`
|
||||
Float64Value float64 `env:"CONFIG_TEST_FLOAT64" cmd:"float64 float64 value"`
|
||||
}
|
||||
|
||||
type DefValueConfig struct {
|
||||
BoolValue bool `env:"CONFIG_TEST_BOOL" cmd:"bool boolean value" default:"true"`
|
||||
IntValue int `env:"CONFIG_TEST_INT" cmd:"int int value" default:"123"`
|
||||
Float64Value float64 `env:"CONFIG_TEST_FLOAT64" cmd:"float64 float64 value" default:"123.4567"`
|
||||
StrValue string `env:"CONFIG_TEST_STR" cmd:"str string value" default:"default-string"`
|
||||
SliceValue []string `env:"CONFIG_TEST_SLICE" cmd:"slice slice values" default:"xx:yy:zz"`
|
||||
NoDefValue string `env:"CONFIG_TEST_NO_DEFVALUE" cmd:"nodefvalue no default value"`
|
||||
}
|
||||
|
||||
type SlicesConfig struct {
|
||||
Paths []string `env:"CONFIG_TEST_SLICES_PATHS"`
|
||||
Debugs []string `env:"CONFIG_TEST_SLICES_DEBUG" separator:";"`
|
||||
Values []int `env:"CONFIG_TEST_SLICES_VALUES" separator:","`
|
||||
}
|
@ -1,8 +1,10 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func SetValueWithBool(v reflect.Value, boolValue string) error {
|
||||
@ -45,69 +47,51 @@ func SetValueWithUintX(v reflect.Value, uintValue string, bitSize int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
/*
|
||||
func setFieldValueWithSlice(name string, v reflect.Value, envValue string,
|
||||
separator string) error {
|
||||
data := strings.Split(envValue, separator)
|
||||
func SetValueWithSlice(v reflect.Value, slice string, separator string) error {
|
||||
data := strings.Split(slice, separator)
|
||||
size := len(data)
|
||||
if size > 0 {
|
||||
slice := reflect.MakeSlice(v.Type(), size, size)
|
||||
for i := 0; i < size; i++ {
|
||||
ele := slice.Index(i)
|
||||
kind := ele.Kind()
|
||||
var err error
|
||||
switch kind {
|
||||
case reflect.Bool:
|
||||
if err := setFieldValueWithBool(name, ele, data[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
err = SetValueWithBool(ele, data[i])
|
||||
case reflect.String:
|
||||
ele.SetString(data[i])
|
||||
case reflect.Uint8:
|
||||
if err := setFieldValueWithUintX(name, ele, data[i], 8); err != nil {
|
||||
return err
|
||||
}
|
||||
err = SetValueWithUintX(ele, data[i], 8)
|
||||
case reflect.Uint16:
|
||||
if err := setFieldValueWithUintX(name, ele, data[i], 16); err != nil {
|
||||
return err
|
||||
}
|
||||
err = SetValueWithUintX(ele, data[i], 16)
|
||||
case reflect.Uint, reflect.Uint32:
|
||||
if err := setFieldValueWithUintX(name, ele, data[i], 32); err != nil {
|
||||
return err
|
||||
}
|
||||
err = SetValueWithUintX(ele, data[i], 32)
|
||||
case reflect.Uint64:
|
||||
if err := setFieldValueWithUintX(name, ele, data[i], 64); err != nil {
|
||||
return err
|
||||
}
|
||||
err = SetValueWithUintX(ele, data[i], 64)
|
||||
case reflect.Int8:
|
||||
if err := setFieldValueWithIntX(name, ele, data[i], 8); err != nil {
|
||||
return err
|
||||
}
|
||||
err = SetValueWithIntX(ele, data[i], 8)
|
||||
case reflect.Int16:
|
||||
if err := setFieldValueWithIntX(name, ele, data[i], 16); err != nil {
|
||||
return err
|
||||
}
|
||||
err = SetValueWithIntX(ele, data[i], 16)
|
||||
case reflect.Int, reflect.Int32:
|
||||
if err := setFieldValueWithIntX(name, ele, data[i], 32); err != nil {
|
||||
return err
|
||||
}
|
||||
err = SetValueWithIntX(ele, data[i], 32)
|
||||
case reflect.Int64:
|
||||
if err := setFieldValueWithIntX(name, ele, data[i], 64); err != nil {
|
||||
return err
|
||||
}
|
||||
err = SetValueWithIntX(ele, data[i], 64)
|
||||
case reflect.Float32:
|
||||
if err := setFieldValueWithFloatX(name, ele, data[i], 32); err != nil {
|
||||
return err
|
||||
}
|
||||
err = SetValueWithFloatX(ele, data[i], 32)
|
||||
case reflect.Float64:
|
||||
if err := setFieldValueWithFloatX(name, ele, data[i], 64); err != nil {
|
||||
return err
|
||||
}
|
||||
err = SetValueWithFloatX(ele, data[i], 64)
|
||||
default:
|
||||
return fmt.Errorf("%s: can't support type: %s", name, kind.String())
|
||||
return fmt.Errorf("Can't support type: %s", kind.String())
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
v.Set(slice)
|
||||
}
|
||||
|
||||
return nil
|
||||
}*/
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user