2
0
This commit is contained in:
eschao 2017-12-08 21:02:26 +08:00
parent 816f4f97dc
commit 8f6edca654
9 changed files with 723 additions and 9 deletions

51
.gitignore vendored
View File

@ -1,14 +1,47 @@
# Binaries for programs and plugins # Compiled Object files, Static and Dynamic libs (Shared Objects)
*.exe *.o
*.dll *.a
*.so *.so
*.dylib
# Test binary, build with `go test -c` # Folders
_obj
_test
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
*.exe
*.test *.test
*.prof
# Output of the go coverage tool, specifically when used with LiteIDE # OS generated files #
*.out .DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db
# Eclipse generated files #
.project
.classpath
.settings
**/target/
*/bin/
.metadata/
clientdb.xml
# jazz files #
.jazzignore
.factorypath
.gitignore
# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736
.glide/

118
cli/cli.go Normal file
View File

@ -0,0 +1,118 @@
package cli
import (
"flag"
"fmt"
"reflect"
"strings"
)
type CliFlag struct {
Name string
Usage string
DefValue *string
CliFlags *[]CliFlag
}
type ValueFlag struct {
Value reflect.Value
}
func (this *ValueFlag) String() string {
return this.Value.Kind().String()
}
func (this *ValueFlag) Set(v string) error {
return nil
}
func Parse(i interface{}, cliFlag *CliFlag) 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 type instead of %s",
valueOfStruct.Kind().String())
}
return parseValue(valueOfStruct, cliFlag)
}
func parseValue(v reflect.Value, cliParent *CliFlag) error {
cliFlags := []CliFlag{}
typeOfStruct := v.Type()
for i := 0; i < v.NumField(); i++ {
valueOfField := v.Field(i)
kindOfField := valueOfField.Kind()
structOfField := typeOfStruct.Field(i)
if kindOfField == reflect.Ptr {
if !valueOfField.IsNil() && valueOfField.CanSet() {
cliFlag := createCliFlagFromTag(structOfField.Tag)
if cliFlag != nil {
cliFlags = append(cliFlags, *cliFlag)
} else {
cliFlag = cliParent
}
Parse(valueOfField.Interface(), cliFlag)
} else {
continue
}
} else if kindOfField == reflect.Struct {
cliFlag := createCliFlagFromTag(structOfField.Tag)
if cliFlag != nil {
cliFlags = append(cliFlags, *cliFlag)
} else {
cliFlag = cliParent
}
parseValue(valueOfField, cliFlag)
}
if cliFlag := installFlag(valueOfField, structOfField); cliFlag != nil {
cliFlags = append(cliFlags, *cliFlag)
}
}
if len(cliFlags) > 0 {
cliParent.CliFlags = &cliFlags
}
return nil
}
func installFlag(v reflect.Value, f reflect.StructField) *CliFlag {
cliFlag := createCliFlagFromTag(f.Tag)
if cliFlag != nil {
vFlag := ValueFlag{Value: v}
flag.Var(&vFlag, cliFlag.Name, cliFlag.Usage)
}
fmt.Printf("Installed flag: %d", cliFlag.Name)
return cliFlag
}
func createCliFlagFromTag(tag reflect.StructTag) *CliFlag {
cliTag, ok := tag.Lookup("cli")
if !ok || cliTag == "" {
return nil
}
cliFlag := CliFlag{}
firstSpace := strings.Index(cliTag, " ")
cliFlag.Name = cliTag
if firstSpace > 0 {
cliFlag.Name = cliTag[0:firstSpace]
cliFlag.Usage = cliTag[firstSpace+1:]
}
defValue, ok := tag.Lookup("default")
if !ok {
cliFlag.DefValue = &defValue
}
return &cliFlag
}

38
cli/cli_test.go Normal file
View File

@ -0,0 +1,38 @@
package cli
import (
"flag"
"fmt"
"testing"
)
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"`
}
type MyCli struct {
Server string `cli:"-server server URL"`
DBCli DBCli `cli:"database database information"`
}
func TestMain(m *testing.M) {
}
func TestCli(t *testing.T) {
hostname := flag.String("hostanme", "127.0.0.1", "hostanme value")
fmt.Printf("Hostname: %s", hostname)
cli := DBCli{}
root := CliFlag{}
err := Parse(&cli, &root)
if err != nil {
t.Errorf("Can't parse cli. %s", err.Error())
}
flag.Parse()
}

47
config.go Normal file
View File

@ -0,0 +1,47 @@
package config
import (
"encoding/json"
"errors"
"io/ioutil"
"reflect"
)
type Field struct {
JsonName string
YamlName string
PropName string
EnvName string
CliName string
Value reflect.Value
DefaultValue string
Separator string
}
type Config struct {
Fields []Field
}
func (this *Config) Init() *Config {
if this.Fields == nil {
this.Fields = []Field{}
}
return this
}
func (this *Config) ParseJSON(jsonFile string, data interface{}) error {
raw, err := ioutil.ReadFile(jsonFile)
if err != nil {
return errors.New("Can't open json file. Err: " + err.Error())
}
err = json.Unmarshal(raw, data)
if err != nil {
return errors.New("Failed unmarshal json. Err: " + err.Error())
}
//fmt.Printf("Data: %v", *data.(*interface{}))
return nil
}

27
config_test.go Normal file
View File

@ -0,0 +1,27 @@
package config
import (
"testing"
)
type TestConfig struct {
Name string `json:"name" default:"test-name"`
Path string `json:"path" default:"./"`
}
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())
}
if myConfig.Name != "jsonconfig" {
t.Errorf("Name json value: %s != jsonconfig", myConfig.Name)
}
if myConfig.Path != "/var" {
t.Errorf("Path json value: %s != /var", myConfig.Path)
}
}

243
env/env.go vendored Normal file
View File

@ -0,0 +1,243 @@
package env
import (
"fmt"
"os"
"reflect"
"strconv"
"strings"
)
func Unmarshal(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 unmarshal(valueOfStruct)
}
func unmarshal(v reflect.Value) error {
typeOfStruct := v.Type()
for i := 0; i < v.NumField(); 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())
} else {
continue
}
} else if kindOfField == reflect.Struct {
unmarshal(valueOfField)
}
if err := setFieldValue(valueOfField, structOfField); err != nil {
return err
}
}
return nil
}
func getEnvValue(envName string, f reflect.StructField) (string, bool) {
envValue, ok := os.LookupEnv(envName)
if !ok {
envValue, ok = f.Tag.Lookup("default")
}
return envValue, ok
}
func setFieldValue(v reflect.Value, f reflect.StructField) error {
envName := f.Tag.Get("env")
if envName == "" {
return nil
}
envValue, ok := getEnvValue(envName, f)
if !ok {
return nil
}
if !v.CanSet() {
return fmt.Errorf("%s: can't be set", f.Name)
}
kind := v.Kind()
name := f.Name
switch kind {
case reflect.Bool:
return setFieldValueWithBool(name, v, envValue)
case reflect.String:
v.SetString(envValue)
return nil
case reflect.Int8:
return setFieldValueWithIntX(name, v, envValue, 8)
case reflect.Int16:
return setFieldValueWithIntX(name, v, envValue, 16)
case reflect.Int, reflect.Int32:
return setFieldValueWithIntX(name, v, envValue, 32)
case reflect.Int64:
return setFieldValueWithIntX(name, v, envValue, 64)
case reflect.Uint8:
return setFieldValueWithUintX(name, v, envValue, 8)
case reflect.Uint16:
return setFieldValueWithUintX(name, v, envValue, 16)
case reflect.Uint, reflect.Uint32:
return setFieldValueWithUintX(name, v, envValue, 32)
case reflect.Uint64:
return setFieldValueWithUintX(name, v, envValue, 64)
case reflect.Float32:
return setFieldValueWithFloatX(name, v, envValue, 32)
case reflect.Float64:
return setFieldValueWithFloatX(name, v, envValue, 64)
case reflect.Slice:
sp, ok := f.Tag.Lookup("separator")
if !ok {
sp = ":"
}
return setFieldValueWithSlice(name, 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())
}
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
}

194
env/env_test.go vendored Normal file
View File

@ -0,0 +1,194 @@
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
}
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"
)
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 unsetEnvConfig1() {
os.Unsetenv("CONFIG_TEST_HOSTNAME")
os.Unsetenv("CONFIG_TEST_PORT")
os.Unsetenv("CONFIG_TEST_USER")
os.Unsetenv("CONFIG_TEST_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))
}
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])
}
}
return true, nil
}
func TestEnvConfig1(t *testing.T) {
setEnvConfig1()
defer unsetEnvConfig1()
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())
}
}
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()
conf := EnvConfig1{}
err := Unmarshal(&conf)
if err != nil {
t.Errorf("Can't unmarshal config1 from environemnt variables. %s",
err.Error())
return
}
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)
}
}
type EnvConfig2 struct {
Config1 EnvConfig1
Server string `env:"CONFIG_ENV_TEST_SERVER"`
}
const (
TEST_SERVER = "test-server"
)
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)
}
}

10
main.go Normal file
View File

@ -0,0 +1,10 @@
package main
import (
"fmt"
)
func main() {
cli := DBCli{}
}

4
test.json Normal file
View File

@ -0,0 +1,4 @@
{
"name": "jsonconfig",
"path": "/var"
}