From 8e63f20a7d51d1a428e0294af62549639ef02c64 Mon Sep 17 00:00:00 2001 From: shockerli Date: Wed, 24 Mar 2021 16:41:57 +0800 Subject: [PATCH] add KeysE support * add KeysE() * TimeE() add tests for fmt.Stringer * dump type of value, for unsupported type --- cvte.go | 23 +++++++++++++++++++++-- cvte_test.go | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 2 deletions(-) diff --git a/cvte.go b/cvte.go index 25c22f3..2cdc9bd 100644 --- a/cvte.go +++ b/cvte.go @@ -534,7 +534,7 @@ func ColumnsE(val interface{}, field interface{}) (sl []interface{}, err error) return } - return nil, fmt.Errorf("unsupported type: %s", rt.Kind()) + return nil, fmt.Errorf("unsupported type: %s", rt.Name()) } func ColumnsIntE(val interface{}, field interface{}) (sl []int, err error) { @@ -605,6 +605,25 @@ func ColumnsStringE(val interface{}, field interface{}) (sl []string, err error) return } +// KeysE return the keys of map, sorted by asc +func KeysE(val interface{}) (sl []interface{}, err error) { + if val == nil { + return nil, errUnsupportedTypeNil + } + + _, rt, rv := indirect(val) + + switch rt.Kind() { + case reflect.Map: + for _, key := range sortedMapKeys(rv) { + sl = append(sl, key.Interface()) + } + return + } + + return nil, fmt.Errorf("unsupported type: %s", rt.Name()) +} + // returns the value with base type func indirect(a interface{}) (val interface{}, rt reflect.Type, rv reflect.Value) { if a == nil { @@ -668,7 +687,7 @@ func newErr(val interface{}, t string) error { // catching an error and return a new func catch(t string, val interface{}, e error) error { if e != nil { - if errors.Is(e, errConvFail) { + if errors.Is(e, errConvFail) || errors.Is(e, errFieldNotFound) || errors.Is(e, errUnsupportedTypeNil) { return newErr(val, t) } return fmt.Errorf(formatExtend, newErr(val, t), e) diff --git a/cvte_test.go b/cvte_test.go index fcc4054..7dda22c 100644 --- a/cvte_test.go +++ b/cvte_test.go @@ -79,6 +79,20 @@ type TestStructE struct { DD *TestStructD } +type TestTimeStringer struct { + time time.Time +} + +func (t TestTimeStringer) String() string { + return t.time.String() +} + +func Benchmark(b *testing.B) { + for i := 0; i < b.N; i++ { + cvt.Bool(aliasTypeString_0, true) + } +} + // [function tests] func TestBoolE(t *testing.T) { @@ -1538,6 +1552,7 @@ func TestTimeE(t *testing.T) { {uint64(1234567890), time.Date(2009, 2, 13, 23, 31, 30, 0, loc), false}, {uint32(1234567890), time.Date(2009, 2, 13, 23, 31, 30, 0, loc), false}, {time.Date(2009, 2, 13, 23, 31, 30, 0, loc), time.Date(2009, 2, 13, 23, 31, 30, 0, loc), false}, + {TestTimeStringer{time.Date(2010, 3, 7, 0, 0, 0, 0, loc)}, time.Date(2010, 3, 7, 0, 0, 0, 0, loc), false}, // errors {"2006", time.Time{}, true}, @@ -1718,3 +1733,37 @@ func TestColumnsFloat64E(t *testing.T) { assert.Equal(t, tt.expect, v, msg) } } + +func TestKeysE(t *testing.T) { + tests := []struct { + input interface{} + expect []interface{} + isErr bool + }{ + {map[int]map[string]interface{}{1: {"1": 111, "DDD": 12.3}, 2: {"2": 222, "DDD": "321"}, 3: {"DDD": nil}}, []interface{}{1, 2, 3}, false}, + {map[string]interface{}{"A": 1, "2": 2}, []interface{}{"2", "A"}, false}, + {map[float64]TestStructD{1: {11}, 2: {22}}, []interface{}{float64(1), float64(2)}, false}, + {map[interface{}]interface{}{1: 1, 2.2: 2.22, "A": "A"}, []interface{}{1, 2.2, "A"}, false}, + + // errors + {nil, nil, true}, + {"Name", nil, true}, + {testing.T{}, nil, true}, + } + + for i, tt := range tests { + msg := fmt.Sprintf( + "i = %d, input[%+v], expect[%+v], isErr[%v]", + i, tt.input, tt.expect, tt.isErr, + ) + + v, err := cvt.KeysE(tt.input) + if tt.isErr { + assert.Error(t, err, msg) + continue + } + + assert.NoError(t, err, msg) + assert.Equal(t, tt.expect, v, msg) + } +}