From c03a9cb48affe8e13d20f4897ac1f1b03365772a Mon Sep 17 00:00:00 2001 From: what Date: Thu, 9 May 2024 10:59:14 +0800 Subject: [PATCH] first commit --- go.mod | 5 ++ go.sum | 12 ++++ utils.go | 175 ++++++++++++++++++++++++++++++++++++++++++++++++++ utils_test.go | 46 +++++++++++++ 4 files changed, 238 insertions(+) create mode 100644 go.mod create mode 100644 go.sum create mode 100755 utils.go create mode 100755 utils_test.go diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..a4a6d2a --- /dev/null +++ b/go.mod @@ -0,0 +1,5 @@ +module git.fsdpf.net/go/utils + +go 1.21 + +require github.com/spf13/cast v1.6.0 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..be51e4e --- /dev/null +++ b/go.sum @@ -0,0 +1,12 @@ +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= +github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= diff --git a/utils.go b/utils.go new file mode 100755 index 0000000..8e7e935 --- /dev/null +++ b/utils.go @@ -0,0 +1,175 @@ +package utils + +import ( + "fmt" + "os" + "path/filepath" + "reflect" + "strings" + "unsafe" + + "github.com/spf13/cast" +) + +func GetFieldValue(i any, k string) any { + v := reflect.ValueOf(i) + + var kv reflect.Value + + if v.Kind() == reflect.Ptr { + v = reflect.Indirect(v) + } + + if v.Kind() == reflect.Struct { + kv = v.FieldByName(k) + } else if v.Kind() == reflect.Map { + for _, key := range v.MapKeys() { + if key.String() == k { + kv = v.MapIndex(key) + } + } + } + + switch kv.Kind() { + case reflect.String: + return kv.String() + case reflect.Bool: + return kv.Bool() + case reflect.Float32, reflect.Float64: + return kv.Float() + case reflect.Int, reflect.Int64: + return int(kv.Int()) + case reflect.Invalid: + return nil + } + + return kv.Interface() +} + +func GetStructField(item any, key string) (field reflect.StructField, ok bool) { + vItem := reflect.TypeOf(item) + + if vItem.Kind() == reflect.Ptr { + vItem = vItem.Elem() + } + + if vItem.Kind() != reflect.Struct { + return field, false + } + + return vItem.FieldByName(UcFirst(key)) +} + +func InterfaceToSlice(i any) (ret []any) { + rv := reflect.ValueOf(i) + + if rv.Kind() == reflect.Ptr { + rv = reflect.Indirect(rv) + } + + if rv.Kind() != reflect.Slice { + panic("请传入切片类型数据") + } + + for i := 0; i < rv.Len(); i++ { + ret = append(ret, rv.Index(i).Interface()) + } + + return ret +} + +func SliceConvert(origSlice any, newSliceType reflect.Type) any { + sv := reflect.ValueOf(origSlice) + + if sv.Kind() != reflect.Slice { + panic(fmt.Sprintf("无效切片类型 %T (来源)", origSlice)) + } + + if newSliceType.Kind() != reflect.Slice { + panic(fmt.Sprintf("无效切片类型 %T (目标)", newSliceType)) + } + + // 生成新类型的切片 + newSlice := reflect.New(newSliceType) + + // hdr指向到新生成切片的SliceHeader + hdr := (*reflect.SliceHeader)(unsafe.Pointer(newSlice.Pointer())) + + var newElemSize = int(sv.Type().Elem().Size()) / int(newSliceType.Elem().Size()) + + // 设置SliceHeader的Cap,Len,以及数组的ptr + hdr.Cap = sv.Cap() * newElemSize + hdr.Len = sv.Len() * newElemSize + hdr.Data = uintptr(sv.Pointer()) + + return newSlice.Elem().Interface() +} + +func DeepSearch(m map[string]any, path []string) map[string]any { + for _, k := range path { + m2, ok := m[k] + if !ok { + // intermediate key does not exist + // => create it and continue from there + m3 := make(map[string]interface{}) + m[k] = m3 + m = m3 + continue + } + m3, ok := m2.(map[string]interface{}) + if !ok { + // intermediate key is a value + // => replace with a new map + m3 = make(map[string]interface{}) + m[k] = m3 + } + // continue search from here + m = m3 + } + return m +} + +func MergeMap(shadow map[string]any, m map[string]any) map[string]any { + if shadow == nil { + shadow = make(map[string]any) + } + + var m2 map[string]any + + for k, val := range m { + switch val.(type) { + case map[string]any: + m2 = val.(map[string]any) + case map[any]any: + m2 = cast.ToStringMap(val) + default: + // immediate value + shadow[k] = val + continue + } + // recursively merge to shadow map + shadow[k] = MergeMap(cast.ToStringMap(shadow[k]), m2) + } + return shadow +} + +func UcFirst(s string) string { + if s == "" { + return "" + } + return strings.ToUpper(s[:1]) + s[1:] +} + +func StudlyCase(s string) string { + return strings.Replace(strings.Title(strings.Replace(s, "-", " ", -1)), " ", "", -1) +} + +func GetAbsPath(s string) string { + if strings.HasPrefix(s, "/") { + return s + } else if dir, err := os.Getwd(); err == nil { + return filepath.Join(dir, s) + } + + return s +} diff --git a/utils_test.go b/utils_test.go new file mode 100755 index 0000000..3142210 --- /dev/null +++ b/utils_test.go @@ -0,0 +1,46 @@ +package utils + +import ( + "testing" +) + +func TestDeepSearch(t *testing.T) { + m := map[string]any{ + "a": 1, + "b": map[string]any{ + "c": 2, + }, + } + + mb := DeepSearch(m, []string{"b", "a"}) + + t.Log(m) + + mb["cc"] = "3" + + t.Log(m) +} + +func TestMergeMap(t *testing.T) { + m1 := map[string]any{ + "a": 1, + "b": map[string]any{ + "c": 2, + "a": 1, + "b": map[string]any{ + "c": 2, + "a": 1, + }, + }, + } + + m2 := map[string]any{ + "a": 11, + "b": map[string]any{ + "c": 22, + "cc": 33, + }, + } + + t.Log(MergeMap(m1, m2)) +}