Skip to content

Commit

Permalink
add func FieldE
Browse files Browse the repository at this point in the history
return the field value from map/struct, ignore the filed type
  • Loading branch information
shockerli committed Mar 11, 2021
1 parent bc76cd1 commit 7648282
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 0 deletions.
24 changes: 24 additions & 0 deletions cvte.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
)

var errConvFail = errors.New("convert failed")
var errFieldNotFound = errors.New("field not found")
var formatOutOfLimitInt = "%w, out of max limit value(%d)"
var formatOutOfLimitFloat = "%w, out of max limit value(%f)"
var formatExtend = "%v, %w"
Expand Down Expand Up @@ -392,6 +393,7 @@ func SliceE(val interface{}) (sl []interface{}, err error) {
return nil, newErr(val, "slice")
}

// return the values of struct fields, and deep find the embedded fields
func deepStructValues(rt reflect.Type, rv reflect.Value) (sl []interface{}) {
for j := 0; j < rv.NumField(); j++ {
if rt.Field(j).Anonymous {
Expand All @@ -403,6 +405,28 @@ func deepStructValues(rt reflect.Type, rv reflect.Value) (sl []interface{}) {
return
}

// FieldE return the field value from map/struct, ignore the filed type
func FieldE(val interface{}, field interface{}) (interface{}, error) {
sf := String(field) // match with the String of field, so field can be any type
_, rt, rv := Indirect(val)

switch rt.Kind() {
case reflect.Map: // key of map
for _, key := range rv.MapKeys() {
if String(key.Interface()) == sf {
return rv.MapIndex(key).Interface(), nil
}
}
case reflect.Struct: // field of struct
vv := rv.FieldByName(sf)
if vv.IsValid() {
return vv.Interface(), nil
}
}

return nil, fmt.Errorf("%w(%s)", errFieldNotFound, sf)
}

// Indirect returns the value with base type
func Indirect(a interface{}) (val interface{}, rt reflect.Type, rv reflect.Value) {
if a == nil {
Expand Down
49 changes: 49 additions & 0 deletions cvte_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import (
"github.com/shockerli/cvt"
)

// [test data]

// alias type: bool
type AliasTypeBool bool

Expand Down Expand Up @@ -64,6 +66,10 @@ type TestStructC struct {
C1 string
}

func (c TestStructC) String() string {
return c.C1
}

type TestStructD struct {
D1 int
}
Expand All @@ -73,6 +79,8 @@ type TestStructE struct {
DD *TestStructD
}

// [function tests]

func TestBoolE(t *testing.T) {
tests := []struct {
input interface{}
Expand Down Expand Up @@ -1389,3 +1397,44 @@ func TestSliceE(t *testing.T) {
assert.Equal(t, tt.expect, v, msg)
}
}

func TestFieldE(t *testing.T) {
tests := []struct {
input interface{}
field interface{}
expect interface{}
isErr bool
}{
{TestStructE{D1: 1, DD: &TestStructD{D1: 2}}, "D1", 1, false},
{TestStructE{D1: 1, DD: &TestStructD{D1: 2}}, "DD", &TestStructD{D1: 2}, false},
{TestStructB{B1: 1, TestStructC: TestStructC{C1: "c1"}}, "C1", "c1", false},
{map[int]interface{}{123: "112233"}, "123", "112233", false},
{map[int]interface{}{123: "112233"}, 123, "112233", false},
{map[string]interface{}{"123": "112233"}, 123, "112233", false},
{map[string]interface{}{"c": "ccc"}, TestStructC{C1: "c"}, "ccc", false},

// errors
{TestStructE{D1: 1, DD: &TestStructD{D1: 2}}, "", nil, true},
{TestStructE{D1: 1, DD: &TestStructD{D1: 2}}, "Age", nil, true},
{int(123), "Name", nil, true},
{uint16(123), "Name", nil, true},
{float64(12.3), "Name", nil, true},
{func() {}, "Name", nil, true},
}

for i, tt := range tests {
msg := fmt.Sprintf(
"i = %d, input[%+v], field[%s], expect[%+v], isErr[%v]",
i, tt.input, tt.field, tt.expect, tt.isErr,
)

v, err := cvt.FieldE(tt.input, tt.field)
if tt.isErr {
assert.Error(t, err, msg)
continue
}

assert.NoError(t, err, msg)
assert.Equal(t, tt.expect, v, msg)
}
}

0 comments on commit 7648282

Please sign in to comment.