diff --git a/archaius.go b/archaius.go index 3c8ba06f..a0c22c85 100755 --- a/archaius.go +++ b/archaius.go @@ -7,12 +7,12 @@ import ( "fmt" filesource "github.com/go-chassis/go-archaius/source/file" - "github.com/spf13/cast" "os" "strings" "github.com/go-chassis/go-archaius/event" + "github.com/go-chassis/go-archaius/pkg/cast" "github.com/go-chassis/go-archaius/source" "github.com/go-chassis/go-archaius/source/cli" "github.com/go-chassis/go-archaius/source/env" @@ -162,9 +162,15 @@ func Get(key string) interface{} { } //GetValue return interface -func GetValue(key string) interface{} { - - return manager.GetConfig(key) +func GetValue(key string) cast.Value { + var confValue cast.Value + val := manager.GetConfig(key) + if val == nil { + confValue = cast.NewValue(nil, source.ErrKeyNotExist) + } else { + confValue = cast.NewValue(val, nil) + } + return confValue } // Exist check the configuration key existence @@ -179,52 +185,47 @@ func UnmarshalConfig(obj interface{}) error { // GetBool is gives the key value in the form of bool func GetBool(key string, defaultValue bool) bool { - v := GetValue(key) - if v == nil { + b, err := GetValue(key).ToBool() + if err != nil { return defaultValue } - r := cast.ToBool(v) - return r + return b } // GetFloat64 gives the key value in the form of float64 func GetFloat64(key string, defaultValue float64) float64 { - v := GetValue(key) - if v == nil { + result, err := GetValue(key).ToFloat64() + if err != nil { return defaultValue } - r := cast.ToFloat64(v) - return r + return result } // GetInt gives the key value in the form of GetInt func GetInt(key string, defaultValue int) int { - v := GetValue(key) - if v == nil { + result, err := GetValue(key).ToInt() + if err != nil { return defaultValue } - r := cast.ToInt(v) - return r + return result } // GetInt64 gives the key value in the form of int64 func GetInt64(key string, defaultValue int64) int64 { - v := GetValue(key) - if v == nil { + result, err := GetValue(key).ToInt64() + if err != nil { return defaultValue } - r := cast.ToInt64(v) - return r + return result } // GetString gives the key value in the form of GetString func GetString(key string, defaultValue string) string { - v := GetValue(key) - if v == nil { + result, err := GetValue(key).ToString() + if err != nil { return defaultValue } - r := cast.ToString(v) - return r + return result } // GetConfigs gives the information about all configurations diff --git a/pkg/cast/cast.go b/pkg/cast/cast.go new file mode 100644 index 00000000..202b8e64 --- /dev/null +++ b/pkg/cast/cast.go @@ -0,0 +1,291 @@ +// Copyright © 2014 Steve Francia . +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file. + +// Package cast provides easy and safe casting in Go. + +// Forked from github.com/spf13/cast +// Some parts of this file has been modified to make it functional in this package + +// Package cast provides the typeCasting of an object +package cast + +import ( + "fmt" + "reflect" + "strconv" + + ca "github.com/spf13/cast" +) + +//const +const ( + fmtToFloat64Failed = "unable to cast %#v of type %T to float64" +) + +type configValue struct { + value interface{} + err error +} + +// NewValue creates an object for an interface X +func NewValue(val interface{}, err error) Value { + confVal := new(configValue) + confVal.value = val + confVal.err = err + return confVal +} + +// Value is an interface to typecast an Object +type Value interface { + ToInt64() (int64, error) + ToInt32() (int32, error) + ToInt16() (int16, error) + ToInt8() (int8, error) + ToInt() (int, error) + ToUint() (uint, error) + ToUint64() (uint64, error) + ToUint32() (uint32, error) + ToUint16() (uint16, error) + ToUint8() (uint8, error) + ToString() (string, error) + ToStringMapStringSlice() (map[string][]string, error) + ToStringMapBool() (map[string]bool, error) + ToStringMap() (map[string]interface{}, error) + ToSlice() ([]interface{}, error) + ToBoolSlice() ([]bool, error) + ToStringSlice() ([]string, error) + ToIntSlice() ([]int, error) + ToBool() (bool, error) + ToFloat64() (float64, error) +} + +func (val *configValue) ToInt64() (int64, error) { + if val.err != nil { + return 0, val.err + } + + return ca.ToInt64E(val.value) +} + +func (val *configValue) ToInt32() (int32, error) { + if val.err != nil { + return 0, val.err + } + + return ca.ToInt32E(val.value) +} + +func (val *configValue) ToInt16() (int16, error) { + if val.err != nil { + return 0, val.err + } + + return ca.ToInt16E(val.value) +} + +func (val *configValue) ToInt8() (int8, error) { + if val.err != nil { + return 0, val.err + } + + return ca.ToInt8E(val.value) +} + +func (val *configValue) ToInt() (int, error) { + if val.err != nil { + return 0, val.err + } + + return ca.ToIntE(val.value) +} + +func (val *configValue) ToUint() (uint, error) { + if val.err != nil { + return 0, val.err + } + + return ca.ToUintE(val.value) +} + +func (val *configValue) ToUint64() (uint64, error) { + if val.err != nil { + return 0, val.err + } + + return ca.ToUint64E(val.value) +} + +func (val *configValue) ToUint32() (uint32, error) { + if val.err != nil { + return 0, val.err + } + + return ca.ToUint32E(val.value) +} + +func (val *configValue) ToUint16() (uint16, error) { + if val.err != nil { + return 0, val.err + } + + return ca.ToUint16E(val.value) +} + +func (val *configValue) ToUint8() (uint8, error) { + if val.err != nil { + return 0, val.err + } + + return ca.ToUint8E(val.value) +} + +func (val *configValue) ToString() (string, error) { + if val.err != nil { + return "", val.err + } + + return ca.ToStringE(val.value) +} + +func (val *configValue) ToStringMapStringSlice() (map[string][]string, error) { + if val.err != nil { + return nil, val.err + } + + return ca.ToStringMapStringSliceE(val.value) +} + +func (val *configValue) ToStringMapBool() (map[string]bool, error) { + if val.err != nil { + return nil, val.err + } + + return ca.ToStringMapBoolE(val.value) +} + +func (val *configValue) ToStringMap() (map[string]interface{}, error) { + if val.err != nil { + return nil, val.err + } + + return ca.ToStringMapE(val.value) +} + +func (val *configValue) ToSlice() ([]interface{}, error) { + if val.err != nil { + return nil, val.err + } + + return ca.ToSliceE(val.value) +} + +func (val *configValue) ToBoolSlice() ([]bool, error) { + if val.err != nil { + return nil, val.err + } + + return ca.ToBoolSliceE(val.value) +} + +func (val *configValue) ToStringSlice() ([]string, error) { + if val.err != nil { + return nil, val.err + } + + return ca.ToStringSliceE(val.value) +} + +func (val *configValue) ToIntSlice() ([]int, error) { + if val.err != nil { + return nil, val.err + } + + return ca.ToIntSliceE(val.value) +} + +func (val *configValue) ToBool() (bool, error) { + value := indirect(val.value) + switch dataType := value.(type) { + case bool: + return dataType, nil + case int: + if value.(int) != 0 { + return true, nil + } + return false, nil + case string: + if len(value.(string)) != 0 { + value, parseError := strconv.ParseBool(dataType) + if parseError != nil { + fmt.Println("error in parsing string to bool", parseError, value) + } + return value, nil + } + return false, nil + default: + return false, fmt.Errorf("unable to cast %#v of type %T to bool", value, value) + } +} + +func (val *configValue) ToFloat64() (float64, error) { + value := indirect(val.value) + + switch dataType := value.(type) { + case float64: + return dataType, nil + case float32: + return float64(dataType), nil + case int: + return float64(dataType), nil + case int64: + return float64(dataType), nil + case int32: + return float64(dataType), nil + case int16: + return float64(dataType), nil + case int8: + return float64(dataType), nil + case uint: + return float64(dataType), nil + case uint64: + return float64(dataType), nil + case uint32: + return float64(dataType), nil + case uint16: + return float64(dataType), nil + case uint8: + return float64(dataType), nil + case nil: + return 0, nil + case string: + floatData, err := parsingString(dataType, value) + return floatData, err + default: + return 0, fmt.Errorf(fmtToFloat64Failed, value, value) + } +} + +func parsingString(dataType string, value interface{}) (float64, error) { + parseValue, parseError := strconv.ParseFloat(dataType, 64) + if parseError == nil { + return parseValue, nil + } + return 0, fmt.Errorf(fmtToFloat64Failed, value, value) +} + +func indirect(val interface{}) interface{} { + if val == nil { + return nil + } + if t := reflect.TypeOf(val); t.Kind() != reflect.Ptr { + // Avoid creating a reflect.value if it's not a pointer. + return val + } + value := reflect.ValueOf(val) + for value.Kind() == reflect.Ptr && !value.IsNil() { + value = value.Elem() + } + return value.Interface() +} diff --git a/pkg/cast/cast_test.go b/pkg/cast/cast_test.go new file mode 100644 index 00000000..eceddecf --- /dev/null +++ b/pkg/cast/cast_test.go @@ -0,0 +1,233 @@ +package cast + +import ( + "errors" + "reflect" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestCast(t *testing.T) { + + var configvalue interface{} + var err error + + t.Log("Test core/cast/cast.go") + assert.NotEqual(t, nil, NewValue("testkey", nil)) + + t.Log("converting the data into int type by ToIntX methods and verifying") + configvalue, err = NewValue(10, errors.New("error")).ToInt64() + assert.Equal(t, errors.New("error"), err) + configvalue, err = NewValue(10, nil).ToInt64() + assert.Equal(t, int64(10), configvalue) + assert.Equal(t, nil, err) + + configvalue, err = NewValue(10, errors.New("error")).ToInt32() + assert.Equal(t, errors.New("error"), err) + configvalue, err = NewValue(10, nil).ToInt32() + assert.Equal(t, int32(10), configvalue) + assert.Equal(t, nil, err) + + configvalue, err = NewValue(10, errors.New("error")).ToInt16() + assert.Equal(t, errors.New("error"), err) + configvalue, err = NewValue(10, nil).ToInt16() + assert.Equal(t, int16(10), configvalue) + assert.Equal(t, nil, err) + + configvalue, err = NewValue(10, errors.New("error")).ToInt8() + assert.Equal(t, errors.New("error"), err) + configvalue, err = NewValue(10, nil).ToInt8() + assert.Equal(t, int8(10), configvalue) + assert.Equal(t, nil, err) + + configvalue, err = NewValue(10, errors.New("error")).ToInt() + assert.Equal(t, errors.New("error"), err) + configvalue, err = NewValue(10, nil).ToInt() + assert.Equal(t, int(10), configvalue) + assert.Equal(t, 10, configvalue) + assert.Equal(t, nil, err) + + configvalue, err = NewValue(10, errors.New("error")).ToUint() + assert.Equal(t, errors.New("error"), err) + configvalue, err = NewValue(10, nil).ToUint() + assert.Equal(t, uint(10), configvalue) + assert.Equal(t, nil, err) + + configvalue, err = NewValue(10, errors.New("error")).ToUint64() + assert.Equal(t, errors.New("error"), err) + configvalue, err = NewValue(10, nil).ToUint64() + assert.Equal(t, uint64(10), configvalue) + assert.Equal(t, nil, err) + + configvalue, err = NewValue(10, errors.New("error")).ToUint32() + assert.Equal(t, errors.New("error"), err) + configvalue, err = NewValue(10, nil).ToUint32() + assert.Equal(t, uint32(10), configvalue) + assert.Equal(t, nil, err) + + configvalue, err = NewValue(10, errors.New("error")).ToUint16() + assert.Equal(t, errors.New("error"), err) + configvalue, err = NewValue(10, nil).ToUint16() + assert.Equal(t, uint16(10), configvalue) + assert.Equal(t, nil, err) + + configvalue, err = NewValue(10, errors.New("error")).ToUint8() + assert.Equal(t, errors.New("error"), err) + configvalue, err = NewValue(10, nil).ToUint8() + assert.Equal(t, uint8(10), configvalue) + assert.Equal(t, nil, err) + + t.Log("verifying ToString method") + configvalue, err = NewValue("hello", errors.New("error")).ToString() + assert.Equal(t, errors.New("error"), err) + configvalue, err = NewValue("hello", nil).ToString() + assert.Equal(t, "hello", configvalue) + assert.Equal(t, nil, err) + configvalue, err = NewValue(10, nil).ToString() + assert.Equal(t, "10", configvalue) + assert.Equal(t, nil, err) + configvalue, err = NewValue(true, nil).ToString() + assert.Equal(t, "true", configvalue) + assert.Equal(t, nil, err) + + t.Log("verifying ToStringMapStringSlice method in different scenarios") + testmap := map[string]string{"testkey1": "test1", "testkey2": "test2"} + expectedmap := make(map[string][]string) + configvalue, err = NewValue(testmap, errors.New("error")).ToStringMapStringSlice() + assert.Equal(t, errors.New("error"), err) + configvalue, err = NewValue(testmap, nil).ToStringMapStringSlice() + assert.Equal(t, nil, err) + if reflect.TypeOf(configvalue) != reflect.TypeOf(expectedmap) { + t.Error("Fileed to get the data in expected(map[string][]string) format") + } + + t.Log("verifying ToStringMapBool method") + testmap2 := map[string]interface{}{"testkey1": "test1", "testkey2": "test2"} + expectedmap2 := make(map[string]bool) + configvalue, err = NewValue(testmap2, errors.New("error")).ToStringMapBool() + assert.Equal(t, errors.New("error"), err) + configvalue, err = NewValue(testmap2, nil).ToStringMapBool() + assert.Equal(t, nil, err) + if reflect.TypeOf(configvalue) != reflect.TypeOf(expectedmap2) { + t.Error("Fileed to get the data in expected(map[string]bool) format") + } + + t.Log("verifying ToStringMap method") + testmap3 := map[interface{}]interface{}{"testkey1": "test1", "testkey2": "test2"} + expectedmap3 := make(map[string]interface{}) + configvalue, err = NewValue(testmap3, errors.New("error")).ToStringMap() + assert.Equal(t, errors.New("error"), err) + configvalue, err = NewValue(testmap3, nil).ToStringMap() + assert.Equal(t, nil, err) + if reflect.TypeOf(configvalue) != reflect.TypeOf(expectedmap3) { + t.Error("Fileed to get the data in expected(map[string]interface{}) format") + } + + t.Log("verifying ToSlice method") + configvalue, err = NewValue("hello", errors.New("error")).ToSlice() + assert.Equal(t, errors.New("error"), err) + configvalue, err = NewValue(testmap3, nil).ToSlice() + assert.NotEqual(t, nil, configvalue) + assert.NotEqual(t, nil, err) + + t.Log("verifying ToBoolSlice method") + testarray := []int{1, 2, 3} + configvalue, err = NewValue(testarray, errors.New("error")).ToBoolSlice() + assert.Equal(t, errors.New("error"), err) + configvalue2, err := NewValue(testarray, nil).ToBoolSlice() + assert.Equal(t, nil, err) + if configvalue2[0] != true || configvalue2[1] != true || configvalue2[2] != true { + t.Error("Fileed to get the data in expected([]bool) format") + } + + t.Log("verifying ToStringSlice method") + teststring := "hello" + var expectedstring []string + configvalue, err = NewValue(teststring, errors.New("error")).ToStringSlice() + assert.Equal(t, errors.New("error"), err) + configvalue, err = NewValue(teststring, nil).ToStringSlice() + assert.Equal(t, nil, err) + if reflect.TypeOf(configvalue) != reflect.TypeOf(expectedstring) { + t.Error("Fileed to get the data in expected([]string) format") + } + + t.Log("verifying ToIntSlice method") + teststring = "hello" + configvalue, err = NewValue(teststring, errors.New("error")).ToIntSlice() + assert.Equal(t, errors.New("error"), err) + configvalue, err = NewValue(teststring, nil).ToIntSlice() + assert.NotEqual(t, nil, configvalue) + assert.NotEqual(t, nil, err) + + t.Log("verifying ToBool method in different scenarios") + var boolkey = false + configvalue, err = NewValue(boolkey, nil).ToBool() + assert.Equal(t, nil, err) + assert.Equal(t, false, configvalue) + configvalue, err = NewValue(10, nil).ToBool() + assert.Equal(t, nil, err) + assert.Equal(t, true, configvalue) + configvalue, err = NewValue(0, nil).ToBool() + assert.Equal(t, nil, err) + assert.Equal(t, false, configvalue) + configvalue, err = NewValue("true", nil).ToBool() + assert.Equal(t, nil, err) + assert.Equal(t, true, configvalue) + configvalue, err = NewValue("f", nil).ToBool() + assert.Equal(t, nil, err) + assert.Equal(t, false, configvalue) + configvalue, err = NewValue("improperstring", nil).ToBool() + assert.Equal(t, nil, err) + assert.Equal(t, false, configvalue) + configvalue, err = NewValue(testmap3, nil).ToBool() + assert.NotEqual(t, nil, err) + assert.Equal(t, false, configvalue) + + t.Log("converting the data into float type by ToFloat64 method and verifying") + configvalue, err = NewValue(float64(10), nil).ToFloat64() + assert.Equal(t, nil, err) + assert.Equal(t, float64(10), configvalue) + configvalue, err = NewValue(10, nil).ToFloat64() + assert.Equal(t, nil, err) + assert.Equal(t, float64(10), configvalue) + configvalue, err = NewValue(int64(10), nil).ToFloat64() + assert.Equal(t, nil, err) + assert.Equal(t, float64(10), configvalue) + configvalue, err = NewValue(int32(10), nil).ToFloat64() + assert.Equal(t, nil, err) + assert.Equal(t, float64(10), configvalue) + configvalue, err = NewValue(int16(10), nil).ToFloat64() + assert.Equal(t, nil, err) + assert.Equal(t, float64(10), configvalue) + configvalue, err = NewValue(int8(10), nil).ToFloat64() + assert.Equal(t, nil, err) + assert.Equal(t, float64(10), configvalue) + configvalue, err = NewValue(uint(10), nil).ToFloat64() + assert.Equal(t, nil, err) + assert.Equal(t, float64(10), configvalue) + configvalue, err = NewValue(uint64(10), nil).ToFloat64() + assert.Equal(t, nil, err) + assert.Equal(t, float64(10), configvalue) + configvalue, err = NewValue(uint32(10), nil).ToFloat64() + assert.Equal(t, nil, err) + assert.Equal(t, float64(10), configvalue) + configvalue, err = NewValue(uint16(10), nil).ToFloat64() + assert.Equal(t, nil, err) + assert.Equal(t, float64(10), configvalue) + configvalue, err = NewValue(uint8(10), nil).ToFloat64() + assert.Equal(t, nil, err) + assert.Equal(t, float64(10), configvalue) + configvalue, err = NewValue(nil, nil).ToFloat64() + assert.Equal(t, nil, err) + assert.Equal(t, float64(0), configvalue) + configvalue, err = NewValue("10", nil).ToFloat64() + assert.Equal(t, nil, err) + assert.Equal(t, float64(10), configvalue) + configvalue, err = NewValue("hello", nil).ToFloat64() + assert.NotEqual(t, nil, err) + assert.Equal(t, float64(0), configvalue) + configvalue, err = NewValue(testmap3, nil).ToFloat64() + assert.NotEqual(t, nil, err) + assert.Equal(t, float64(0), configvalue) +}