diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..485dee6 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.idea diff --git a/builder.go b/builder.go new file mode 100644 index 0000000..9f1c567 --- /dev/null +++ b/builder.go @@ -0,0 +1,8 @@ +package goref + +import "reflect" + +func BuildSlice(t reflect.Type) any { + sliceT := reflect.SliceOf(t) + return reflect.MakeSlice(sliceT, 0, 0).Interface() +} diff --git a/builder_test.go b/builder_test.go new file mode 100644 index 0000000..38a2946 --- /dev/null +++ b/builder_test.go @@ -0,0 +1,17 @@ +package goref_test + +import ( + "gitter.top/common/goref" + "reflect" + "testing" +) + +func TestBuildSliceStruct(t *testing.T) { + type T struct { + A string + } + var a T + at := reflect.TypeOf(a) + value := goref.BuildSlice(at) + t.Log(value.([]T)) +} diff --git a/go.mod b/go.mod index f01554b..fa5ef7a 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,11 @@ -module gitter.top/coco/goref +module gitter.top/common/goref -go 1.20 +go 1.21 + +require github.com/stretchr/testify v1.8.4 + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..fa4b6e6 --- /dev/null +++ b/go.sum @@ -0,0 +1,10 @@ +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/keys.go b/keys.go new file mode 100644 index 0000000..656e8cb --- /dev/null +++ b/keys.go @@ -0,0 +1,27 @@ +package goref + +import "reflect" + +func ExistMapKey(val any, key string) bool { + vo := reflect.ValueOf(val) + if vo.Kind() == reflect.Ptr { + vo = vo.Elem() + } + if vo.Kind() != reflect.Map { + return false + } + getter := vo.MapIndex(reflect.ValueOf(key)) + return getter.IsValid() +} + +func ExistStructField(val any, field string) bool { + vo := reflect.ValueOf(val) + if vo.Kind() == reflect.Ptr { + vo = vo.Elem() + } + if vo.Kind() != reflect.Struct { + return false + } + getter := vo.FieldByName(field) + return getter.IsValid() +} diff --git a/type.go b/type.go index a1b4dc3..c77c140 100644 --- a/type.go +++ b/type.go @@ -139,3 +139,101 @@ func IsChannel(val any) bool { typeOf := reflect.TypeOf(val) return typeOf.Kind() == reflect.Chan } + +func IsBaseStruct(val any) bool { + typeOf := reflect.TypeOf(val) + if typeOf.Kind() == reflect.Ptr { + typeOf = typeOf.Elem() + } + return typeOf.Kind() == reflect.Struct +} + +func IsBaseMap(val any) bool { + typeOf := reflect.TypeOf(val) + if typeOf.Kind() == reflect.Ptr { + typeOf = typeOf.Elem() + } + return typeOf.Kind() == reflect.Map +} + +func IsBaseString(val any) bool { + typeOf := reflect.TypeOf(val) + if typeOf.Kind() == reflect.Ptr { + typeOf = typeOf.Elem() + } + return typeOf.Kind() == reflect.String +} + +func IsBaseSlice(val any) bool { + typeOf := reflect.TypeOf(val) + if typeOf.Kind() == reflect.Ptr { + typeOf = typeOf.Elem() + } + return typeOf.Kind() == reflect.Slice +} + +func IsBaseArray(val any) bool { + typeOf := reflect.TypeOf(val) + if typeOf.Kind() == reflect.Ptr { + typeOf = typeOf.Elem() + } + return typeOf.Kind() == reflect.Array +} + +func IsBaseList(val any) bool { + return IsBaseSlice(val) || IsBaseArray(val) +} + +func GetListBaseType(val any) reflect.Type { + to := reflect.TypeOf(val) + if to.Kind() == reflect.Ptr { + to = to.Elem() + } + if to.Kind() != reflect.Slice && to.Kind() != reflect.Array { + panic("invalid type: " + to.String()) + } + to = to.Elem() + if to.Kind() == reflect.Ptr { + to = to.Elem() + } + return to +} + +func GetMapValueBaseType(val any) reflect.Type { + to := reflect.TypeOf(val) + if to.Kind() == reflect.Ptr { + to = to.Elem() + } + if to.Kind() != reflect.Map { + panic("invalid type: " + to.String()) + } + to = to.Elem() + if to.Kind() == reflect.Ptr { + to = to.Elem() + } + return to +} + +func GetBaseType(val any) reflect.Type { + to := reflect.TypeOf(val) + if to.Kind() == reflect.Ptr { + to = to.Elem() + } + if to.Kind() == reflect.Map || to.Kind() == reflect.Array || to.Kind() == reflect.Slice || to.Kind() == reflect.Chan { + to = to.Elem() + } + if to.Kind() == reflect.Ptr { + to = to.Elem() + } + return to +} + +func IsBasicType(val any) bool { + rt := GetBaseType(val) + switch rt.Kind() { + case reflect.Chan, reflect.Struct, reflect.Array, reflect.Func, reflect.Map, reflect.Slice, reflect.Interface: + return false + default: + return true + } +} diff --git a/type_test.go b/type_test.go new file mode 100644 index 0000000..402b288 --- /dev/null +++ b/type_test.go @@ -0,0 +1,34 @@ +package goref_test + +import ( + "github.com/stretchr/testify/assert" + "gitter.top/common/goref" + "reflect" + "testing" +) + +func TestIsBaseStruct(t *testing.T) { + type T struct{} + var a T + t.Log(goref.IsBaseStruct(a)) + var list any = []T{} + t.Log(reflect.TypeOf(list)) +} + +func TestToInterfaces(t *testing.T) { + type T string + var list any = []T{ + "hello", + "world", + } + t.Log(goref.ToInterfaces(list)) +} + +func TestGetBaseType(t *testing.T) { + var ch = make(chan int, 5) + assert.EqualValues(t, reflect.Int, goref.GetBaseType(ch).Kind()) + var slice []string + assert.EqualValues(t, reflect.String, goref.GetBaseType(slice).Kind()) + var m map[string]struct{} + assert.EqualValues(t, reflect.Struct, goref.GetBaseType(m).Kind()) +} diff --git a/value.go b/value.go new file mode 100644 index 0000000..ebd2082 --- /dev/null +++ b/value.go @@ -0,0 +1,160 @@ +package goref + +import ( + "fmt" + "reflect" + "strings" +) + +func ToString(val any) (string, bool) { + vo := reflect.ValueOf(val) + if vo.Kind() == reflect.Ptr { + vo = vo.Elem() + } + if vo.Kind() == reflect.String { + return vo.String(), true + } + return "", false +} + +func ToStrings(val any) ([]string, bool) { + vo := reflect.ValueOf(val) + if vo.Kind() == reflect.Ptr { + vo = vo.Elem() + } + if vo.Kind() == reflect.Slice || vo.Kind() == reflect.Array { + var list []string + for i := 0; i < vo.Len(); i++ { + val := vo.Index(i).Interface() + valVo := reflect.ValueOf(val) + if valVo.Kind() == reflect.Ptr { + valVo = valVo.Elem() + } + if valVo.Kind() == reflect.String { + list = append(list, valVo.String()) + } + } + return list, true + } + return nil, false +} + +func ToInterfaces(val any) ([]interface{}, bool) { + vo := reflect.ValueOf(val) + if vo.Kind() == reflect.Ptr { + vo = vo.Elem() + } + if vo.Len() == 0 { + return nil, false + } + var result []interface{} + for i := 0; i < vo.Len(); i++ { + result = append(result, vo.Index(i).Interface()) + } + return result, true +} + +type tagger struct { + tag string + omitempty bool +} + +func (t *tagger) merge() { + if t.tag == "" { + t.tag = "-" + } +} + +type taggerOption func(*tagger) + +func WithTag(tag string) taggerOption { + return func(tager *tagger) { + tager.tag = tag + } +} + +func WithOmitempty(omit bool) taggerOption { + return func(tager *tagger) { + tager.omitempty = omit + } +} + +func StructToMap(val any, tagOpts ...taggerOption) (map[string]interface{}, error) { + vo := reflect.ValueOf(val) + if vo.Kind() == reflect.Ptr { + vo = vo.Elem() + } + if vo.Kind() != reflect.Struct { + return nil, fmt.Errorf("val type not struct") + } + var data = make(map[string]interface{}) + var tagger = new(tagger) + for _, opt := range tagOpts { + opt(tagger) + } + tagger.merge() + for i := 0; i < vo.NumField(); i++ { + vf := vo.Field(i) + if tagger.omitempty && vf.IsZero() { + continue + } + key := vo.Type().Field(i).Tag.Get(tagger.tag) + if key == "" { + key = vo.Type().Field(i).Name + } + if strings.Contains(key, ",omitempty") { + key = strings.Replace(key, ",omitempty", "", 1) + } + data[key] = vf.Interface() + } + return data, nil +} + +// SliceStructToMap slice struct to map, field as key +// value type []struct, bind type *map[string]*struct +func SliceStructToMap(value, bind any, field string) error { + // bind type check + if GetListBaseType(value) != GetMapValueBaseType(bind) { + return fmt.Errorf("struct type no match map type") + } + + vo := reflect.ValueOf(value) + if vo.Kind() == reflect.Ptr { + vo = vo.Elem() + } + if vo.Kind() != reflect.Slice && vo.Kind() != reflect.Array { + return fmt.Errorf("val type not array or slice: " + vo.String()) + } + + bo := reflect.ValueOf(bind) + if bo.Kind() == reflect.Ptr { + bo = bo.Elem() + } + if bo.Kind() != reflect.Map { + return fmt.Errorf("invalid map type") + } + if !bo.CanSet() { + return fmt.Errorf("bind map must be initialized") + } + boKey := bo.Type().Key() + boVal := bo.Type().Elem() + bo.Set(reflect.MakeMap(reflect.MapOf(boKey, boVal))) + + for i := 0; i < vo.Len(); i++ { + t := vo.Index(i) + if t.Kind() == reflect.Ptr { + t = t.Elem() + } + idxField := t.FieldByName(field) + if idxField.Kind() == reflect.Invalid { + continue + } + idxVal := vo.Index(i) + if boVal.Kind() == reflect.Ptr { + bo.SetMapIndex(idxField, idxVal.Addr()) + } else { + bo.SetMapIndex(idxField, idxVal) + } + } + return nil +} diff --git a/value_test.go b/value_test.go new file mode 100644 index 0000000..3c4cf65 --- /dev/null +++ b/value_test.go @@ -0,0 +1,31 @@ +package goref_test + +import ( + "github.com/stretchr/testify/assert" + "gitter.top/common/goref" + "testing" +) + +func TestSliceStructToMap(t *testing.T) { + type T struct { + Id string + } + var a []T + a = append(a, T{ + Id: "1", + }) + a = append(a, T{ + Id: "2", + }) + a = append(a, T{ + Id: "3", + }) + a = append(a, T{ + Id: "4", + }) + + var bind = make(map[string]T) + err := goref.SliceStructToMap(a, &bind, "Id") + assert.NoError(t, err) + t.Logf("map: %#v", bind["1"]) +}