diff --git a/.gitignore b/.gitignore index 66fd13c..86bfb61 100644 --- a/.gitignore +++ b/.gitignore @@ -5,7 +5,7 @@ *.so *.dylib -# Test binary, built with `go test -c` +# Test binconv, built with `go test -c` *.test # Output of the go coverage tool, specifically when used with LiteIDE @@ -13,3 +13,42 @@ # Dependency directories (remove the comment below to include it) # vendor/ +go.sum +go.work.sum + +./vendor/ +vendor/ +# IDEA # +.idea/ +*.iml +.DS_Store + +# Log # +logs/ +*.log + +node_modules/ +dist/ + + +# local env files +.env.local +.env.*.local + +# lock file +-lock.* +*.lock + +# Log files +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* + +# Editor directories and files +.vscode/ +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? \ No newline at end of file diff --git a/README.md b/README.md index df75ae1..6e78e99 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,14 @@ # wepkg golang的工具库 -- bean属性拷贝 +- 属性复制 - 分布式雪花算法 -- redis库 -- 常用的工具集 +- 缓存(redis) +- 字符 +- 文件 +- json +- 时间日期 +- 条件表达式 +- 协程池 +- 切片 +... diff --git a/conv/arrayconv/array.go b/conv/arrayconv/array.go new file mode 100644 index 0000000..52fdba3 --- /dev/null +++ b/conv/arrayconv/array.go @@ -0,0 +1,212 @@ +package arrayconv + +import ( + "sort" +) + +type ArrayList []interface{} + +// 添加, 如果存在, 不处理 +func (arr *ArrayList) Add(items ...interface{}) { + for _, item := range items { + if arr.Contains(item) { + return + } + arr.Push(item) + } +} + +func (arr *ArrayList) At(index int) interface{} { + return (*arr)[index] +} + +func (arr *ArrayList) Clear() { + *arr = (*arr)[:0] +} + +func (arr *ArrayList) Contains(item interface{}) bool { + return arr.IndexOf(item) != -1 +} + +func (arr *ArrayList) ContainsCond(cond func(item interface{}) bool) bool { + return arr.IndexOf(cond) != -1 +} + +// 每一个项都符合条件就返回true +func (arr *ArrayList) Every(cond func(item interface{}) bool) bool { + s := *arr + for i := 0; i < len(s); i++ { + val := s[i] + if cond(val) == false { + return false + } + } + return true +} + +func (arr *ArrayList) First(cond func(item interface{}) bool) (val interface{}, has bool) { + s := *arr + for i := 0; i < len(s); i++ { + val := s[i] + if cond(val) { + return val, true + } + } + return nil, false +} + +func (arr *ArrayList) Filter(cond func(index int, elem interface{}) bool) (r ArrayList) { + for i, x := range *arr { + if cond(i, x) { + r = append(r, x) + } + } + return r +} + +func (arr *ArrayList) ForRange(handler func(item interface{})) { + for _, x := range *arr { + handler(x) + } +} + +func (arr *ArrayList) IndexOfConf(cond func(item interface{}) bool) int { + s := *arr + for i := 0; i < len(s); i++ { + if cond(s[i]) { + return i + } + } + return -1 +} + +func (arr *ArrayList) IndexOf(item interface{}) int { + s := *arr + for i := 0; i < len(s); i++ { + if s[i] == item { + return i + } + } + return -1 +} + +func (arr *ArrayList) Last(cond func(item interface{}) bool) interface{} { + s := *arr + for i := len(s) - 1; i >= 0; i-- { + val := s[i] + if cond(val) { + return val + } + } + return nil +} + +func (arr *ArrayList) Length() int { + return len(*arr) +} + +func (arr *ArrayList) Pop() interface{} { + s := *arr + last := s[len(s)-1] + s[len(s)-1] = nil // GC + s2 := s[:len(s)-1] + *arr = s2 + + return last +} + +func (arr *ArrayList) Push(item interface{}) { + s := *arr + s = append(s, item) + *arr = s +} + +func (arr *ArrayList) PushList(list ArrayList) { + s := *arr + s = append(s, list...) + *arr = s +} + +func (arr *ArrayList) Remove(item interface{}) { + i := arr.IndexOf(item) + if i != -1 { + arr.RemoveAt(i) + } +} + +func (arr *ArrayList) RemoveAt(i int) { + s := *arr + copy(s[i:], s[i+1:]) + s[len(s)-1] = nil // GC + s2 := s[:len(s)-1] + *arr = s2 +} + +func (arr *ArrayList) Replace(i int, item interface{}) { + s := *arr + over := i - len(s) + if over > -1 { + ss := make([]interface{}, i+1) + copy(ss[0:], s[:]) + s = ss + } + s[i] = item + *arr = s +} + +func (arr *ArrayList) Reverse() { + for i := len(*arr)/2 - 1; i >= 0; i-- { + opp := len(*arr) - 1 - i + (*arr)[i], (*arr)[opp] = (*arr)[opp], (*arr)[i] + } +} + +func (arr *ArrayList) Shift() interface{} { + s := *arr + top := s[0] + s[0] = nil // GC + s2 := s[1:] + *arr = s2 + + return top +} + +func (arr *ArrayList) Slice() []interface{} { + return []interface{}(*arr) +} + +func (arr *ArrayList) Sort(compare func(a, b interface{}) int) { + l := *arr + sort.Slice(l, func(i, j int) bool { + return compare(l[i], l[j]) >= 0 + }) +} + +func (arr *ArrayList) Unshift(item interface{}) { + s := *arr + l := len(s) + 1 + ss := make([]interface{}, l, l) + ss[0] = item + copy(ss[1:], s[:]) + *arr = ss +} + +// 去重操作, 返回去重后的数组 +func (arr *ArrayList) Unique(getKey func(a interface{}) string) (r ArrayList) { + l := *arr + m := map[string]interface{}{} // 存放不重复主键 + for _, e := range l { + length := len(m) + m[getKey(e)] = 0 + if len(m) != length { // 加入map后,map长度变化,则元素不重复 + r = append(r, e) + } + } + return +} + +// 并集 +func (arr *ArrayList) Union(a ArrayList, getKey func(a interface{}) string) ArrayList { + arr.PushList(a) + return arr.Unique(getKey) +} diff --git a/conv/binconv/binary.go b/conv/binconv/binary.go new file mode 100644 index 0000000..cfcfac8 --- /dev/null +++ b/conv/binconv/binary.go @@ -0,0 +1,107 @@ +package binconv + +import ( + "encoding/binary" + "io" +) + +func WriteBytes(w io.Writer, order binary.ByteOrder, data []byte) { + binary.Write(w, order, int16(len(data))) + binary.Write(w, order, data) + +} + +func WriteUTF(w io.Writer, order binary.ByteOrder, data string) (err error) { + err = binary.Write(w, order, int16(len(data))) + if err != nil { + return + } + err = binary.Write(w, order, []byte(data)) + if err != nil { + return + } + return +} + +func ReadBool(r io.Reader, order binary.ByteOrder) bool { + var val bool + binary.Read(r, binary.BigEndian, &val) + return val +} + +func ReadInt8(r io.Reader, order binary.ByteOrder) int8 { + var val int8 + binary.Read(r, binary.BigEndian, &val) + return val +} + +func ReadInt16(r io.Reader, order binary.ByteOrder) int16 { + var val int16 + binary.Read(r, binary.BigEndian, &val) + return val +} + +func ReadInt32(r io.Reader, order binary.ByteOrder) int32 { + var val int32 + binary.Read(r, binary.BigEndian, &val) + return val +} + +func ReadInt64(r io.Reader, order binary.ByteOrder) int64 { + var val int64 + binary.Read(r, binary.BigEndian, &val) + return val +} + +func ReadUint8(r io.Reader, order binary.ByteOrder) uint8 { + var val uint8 + binary.Read(r, binary.BigEndian, &val) + return val +} + +func ReadUint16(r io.Reader, order binary.ByteOrder) uint16 { + var val uint16 + binary.Read(r, binary.BigEndian, &val) + return val +} + +func ReadUint32(r io.Reader, order binary.ByteOrder) uint32 { + var val uint32 + binary.Read(r, binary.BigEndian, &val) + return val +} + +func ReadUint64(r io.Reader, order binary.ByteOrder) uint64 { + var val uint64 + binary.Read(r, binary.BigEndian, &val) + return val +} + +func ReadFloat32(r io.Reader, order binary.ByteOrder) float32 { + var val float32 + binary.Read(r, binary.BigEndian, &val) + return val +} + +func ReadFloat64(r io.Reader, order binary.ByteOrder) float64 { + var val float64 + binary.Read(r, binary.BigEndian, &val) + return val +} + +func ReadBytes(r io.Reader, order binary.ByteOrder) (data []byte) { + var tlen int16 + binary.Read(r, order, &tlen) + data = make([]byte, tlen) + binary.Read(r, order, &data) + return +} + +func ReadUTF(r io.Reader, order binary.ByteOrder) (data string) { + tlen := int16(0) + binary.Read(r, order, &tlen) + tbs := make([]byte, tlen) + binary.Read(r, order, &tbs) + data = string(tbs) + return +} diff --git a/conv/stringconv/string.go b/conv/stringconv/string.go new file mode 100644 index 0000000..ea0c540 --- /dev/null +++ b/conv/stringconv/string.go @@ -0,0 +1,84 @@ +package stringconv + +import "strings" + +// 驼峰 to 下划线 +func CamelToUnderline(target string) string { + return strings.Join(SplitUpper(target), "_") +} + +// 下划线 to 驼峰 +func UnderlineToCamel(target string) string { + arr := strings.Split(target, "_") + for i, val := range arr { + arr[i] = UpperFirst(val) + } + return strings.Join(arr, "") +} + +// 按照大小写分组 +func SplitUpper(target string) []string { + chars := []byte(target) + result := []string{} + str := "" + for i := 0; i < len(target); i++ { + char := chars[i] + if i == 0 { + + } else if char >= 65 && char <= 90 { + result = append(result, str) + str = "" + } + + str += string(char) + } + + result = append(result, str) + return result +} + +// SplitLast 获取字符串被"."分离后的字符串数组中 +// ,最后一个字符串。 +func SplitLast(str string, sep string) string { + strArr := strings.Split(str, sep) + if strArr != nil && len(strArr) > 0 { + return strArr[len(strArr)-1] + } + + return str +} + +// 首字母是否小写 +func IsLowerFirst(str string) bool { + first := str[0] + if first >= 97 && first <= 122 { + return true + } + return false +} + +// 首字母是否大写 +func IsUpperFirst(str string) bool { + first := str[0] + if first >= 65 && first <= 90 { + return true + } + return false +} + +// LowerFirst 首字母小写 +func LowerFirst(str string) string { + first := str[:1] + return strings.Replace(str, first, strings.ToLower(first), 1) +} + +// UpperFirst 首字母大写 +func UpperFirst(str string) string { + if len(str) == 0 { + return str + } + + first := str[:1] + + return strings.Replace(str, first, strings.ToUpper(first), 1) +} diff --git a/copy/bytes.go b/copy/bytes.go new file mode 100644 index 0000000..1a8fd75 --- /dev/null +++ b/copy/bytes.go @@ -0,0 +1,308 @@ +package copy + +import ( + "encoding/binary" + "errors" + "github.com/wegoteam/wepkg/conv/binconv" + "io" + "reflect" + "strconv" +) + +func (ctx *Context) toBytes(source Value, provideTyp reflect.Type, w io.Writer, depth int) (err error) { + srcref := source.Upper() + //fmt.Println("\n||| to", "provide=", provideTyp) + //fmt.Println("srctyp=", srcref.Type(), "src=", srcref) + + // 源是否空 + if srcref.IsValid() == false { + return + } + if isHard(srcref.Kind()) && srcref.IsNil() { + return + } + + // 接口处理 + if provideTyp.Kind() != reflect.Interface { + if srcref.Kind() == reflect.Interface { + srcref = srcref.Elem() + } + } + + //fmt.Println("last target=", tarref, tarref.Type(), tarref.CanSet()) + switch provideTyp.Kind() { + case reflect.Slice, reflect.Array: + err = binary.Write(w, binary.BigEndian, int16(srcref.Len())) + if srcref.Len() == 0 { + return + } + for i := 0; i < srcref.Len(); i++ { + srcitem := srcref.Index(i) + err = ctx.toBytes(Value(srcitem), provideTyp.Elem(), w, depth+1) + if err != nil { + err = errors.New("at " + strconv.Itoa(i) + ": " + err.Error()) + return + } + } + case reflect.Interface: + err = ctx.toBytes(Value(srcref.Elem()), srcref.Elem().Type(), w, depth+1) + if err != nil { + return + } + case reflect.Ptr: + err = ctx.toBytes(Value(srcref.Elem()), provideTyp.Elem(), w, depth+1) + if err != nil { + return + } + case reflect.Struct: + for _, field := range TypeUtiler.GetFieldRecursion(provideTyp) { + srcfield := getFieldVal(srcref, field) + if srcref.Kind() == reflect.Map { + if srcfield.IsValid() == false || srcfield.IsNil() { + continue + } + } + //fmt.Println(">>> copy struct field: ", field.Name, ", fieldtyp=", field.Type) + err = ctx.toBytes(Value(srcfield), field.Type, w, depth+1) + if err != nil { + err = errors.New(field.Name + ": " + err.Error()) + return + } + } + case reflect.Map: + err = binary.Write(w, binary.BigEndian, int16(srcref.Len())) + if srcref.Len() == 0 { + return + } + + for _, k := range srcref.MapKeys() { + err = binconv.WriteUTF(w, binary.BigEndian, k.String()) + val1 := srcref.MapIndex(k) + if val1.IsValid() == false { + continue + } + //fmt.Println("||| copy map key: ", k, ", fieldtyp=", val1.Type()) + //fmt.Println("src=", val1, ", typ=", val2) + + err = ctx.toBytes(Value(val1), val1.Type(), w, depth+1) + if err != nil { + err = errors.New("at " + k.String() + ": " + err.Error()) + return + } + } + + case reflect.Func: + panic("not suppor") + default: + err = ctx.toBytesProp(provideTyp.Kind(), srcref, w) + if err != nil { + return + } + } + + //fmt.Println("resut >", result.Upper()) + return +} + +func (ctx *Context) toBytesProp(kind reflect.Kind, val reflect.Value, w io.Writer) (err error) { + switch kind { + case reflect.Bool: + err = binary.Write(w, binary.BigEndian, val.Bool()) + case reflect.Int: + err = binary.Write(w, binary.BigEndian, int64(val.Int())) + case reflect.Int8: + err = binary.Write(w, binary.BigEndian, int8(val.Int())) + case reflect.Int16: + err = binary.Write(w, binary.BigEndian, int16(val.Int())) + case reflect.Int32: + err = binary.Write(w, binary.BigEndian, int32(val.Int())) + case reflect.Int64: + err = binary.Write(w, binary.BigEndian, int64(val.Int())) + case reflect.Uint: + err = binary.Write(w, binary.BigEndian, uint64(val.Uint())) + case reflect.Uint8: + err = binary.Write(w, binary.BigEndian, uint8(val.Uint())) + case reflect.Uint16: + err = binary.Write(w, binary.BigEndian, uint16(val.Uint())) + case reflect.Uint32: + err = binary.Write(w, binary.BigEndian, uint32(val.Uint())) + case reflect.Uint64: + err = binary.Write(w, binary.BigEndian, uint64(val.Uint())) + case reflect.Float32: + err = binary.Write(w, binary.BigEndian, float32(val.Float())) + case reflect.Float64: + err = binary.Write(w, binary.BigEndian, val.Float()) + case reflect.String: + err = binconv.WriteUTF(w, binary.BigEndian, val.String()) + default: + err = errors.New("unknown kind") + } + + return +} + +func (ctx *Context) fromBytes(source Value, provideTyp reflect.Type, r io.Reader, depth int) (err error) { + srcref := source.Upper() + //fmt.Println("\n||| to", "provide=", provideTyp) + //fmt.Println("srctyp=", srcref.Type(), "src=", srcref) + + // 源是否空 + if srcref.IsValid() == false { + return + } + if isHard(srcref.Kind()) && srcref.IsNil() { + return + } + + // 接口处理 + if provideTyp.Kind() != reflect.Interface { + if srcref.Kind() == reflect.Interface { + srcref = srcref.Elem() + } + } + + //fmt.Println("last target=", tarref, tarref.Type(), tarref.CanSet()) + switch provideTyp.Kind() { + case reflect.Slice, reflect.Array: + length := int(binconv.ReadInt16(r, binary.BigEndian)) + if length == 0 { + return + } + for i := 0; i < length; i++ { + srcitem := reflect.New(provideTyp.Elem()) + err = ctx.fromBytes(Value(srcitem), provideTyp.Elem(), r, depth+1) + if err != nil { + err = errors.New("at " + strconv.Itoa(i) + ": " + err.Error()) + return + } + } + case reflect.Interface: + err = ctx.fromBytes(Value(srcref.Elem()), srcref.Elem().Type(), r, depth+1) + if err != nil { + return + } + case reflect.Ptr: + err = ctx.fromBytes(Value(srcref.Elem()), provideTyp.Elem(), r, depth+1) + if err != nil { + return + } + case reflect.Struct: + for _, field := range TypeUtiler.GetFieldRecursion(provideTyp) { + srcfield := getFieldVal(srcref, field) + if srcref.Kind() == reflect.Map { + if srcfield.IsValid() == false || srcfield.IsNil() { + continue + } + } + //fmt.Println(">>> copy struct field: ", field.Name, ", fieldtyp=", field.Type) + err = ctx.fromBytes(Value(srcfield), field.Type, r, depth+1) + if err != nil { + err = errors.New(field.Name + ": " + err.Error()) + return + } + } + case reflect.Map: + length := int(binconv.ReadInt16(r, binary.BigEndian)) + if length == 0 { + return + } + for i := 0; i < length; i++ { + k := reflect.ValueOf(binconv.ReadUTF(r, binary.BigEndian)) + + val := reflect.New(provideTyp.Elem()) + err = ctx.fromBytes(Value(val), val.Type(), r, depth+1) + if err != nil { + err = errors.New("at " + k.String() + ": " + err.Error()) + return + } + + srcref.SetMapIndex(k, val) + //fmt.Println("||| copy map key: ", k, ", fieldtyp=", val1.Type()) + //fmt.Println("src=", val1, ", typ=", val2) + + } + + case reflect.Func: + panic("not suppor") + default: + val, fromerr := ctx.fromBytesProp(provideTyp.Kind(), r) + if fromerr != nil { + err = fromerr + return + } + + srcref.Set(val) + } + + //fmt.Println("resut >", result.Upper()) + return +} + +// 获取正确的反射对象,如果nil,创建新的 +func unfoldValue(val reflect.Value) reflect.Value { + var typ = val.Type() + switch typ.Kind() { + case reflect.Struct: + case reflect.Ptr: + typ = typ.Elem() + if val.IsNil() { + var obj = reflect.New(typ) + val.Set(obj) + } + + return val.Elem() + } + + return val +} + +// 获取正确的反射对象,如果nil,创建新的 +func unfoldType(typ reflect.Type) reflect.Type { + switch typ.Kind() { + case reflect.Struct: + case reflect.Ptr: + typ = typ.Elem() + return typ + } + + return typ +} + +func (ctx *Context) fromBytesProp(kind reflect.Kind, r io.Reader) (result reflect.Value, err error) { + var data interface{} + switch kind { + case reflect.Bool: + data = binconv.ReadBool(r, binary.BigEndian) + case reflect.Int: + data = int(binconv.ReadInt64(r, binary.BigEndian)) + case reflect.Int8: + data = binconv.ReadInt8(r, binary.BigEndian) + case reflect.Int16: + data = binconv.ReadInt16(r, binary.BigEndian) + case reflect.Int32: + data = binconv.ReadInt32(r, binary.BigEndian) + case reflect.Int64: + data = binconv.ReadInt64(r, binary.BigEndian) + case reflect.Uint: + data = uint(binconv.ReadUint64(r, binary.BigEndian)) + case reflect.Uint8: + data = binconv.ReadUint8(r, binary.BigEndian) + case reflect.Uint16: + data = binconv.ReadUint16(r, binary.BigEndian) + case reflect.Uint32: + data = binconv.ReadUint32(r, binary.BigEndian) + case reflect.Uint64: + data = binconv.ReadUint64(r, binary.BigEndian) + case reflect.Float32: + data = binconv.ReadFloat32(r, binary.BigEndian) + case reflect.Float64: + data = binconv.ReadFloat64(r, binary.BigEndian) + case reflect.String: + data = binconv.ReadUTF(r, binary.BigEndian) + default: + err = errors.New("not support default kind: " + kind.String()) + } + + result = reflect.ValueOf(data) + return +} diff --git a/copy/compare-expr.go b/copy/compare-expr.go new file mode 100644 index 0000000..6d466b6 --- /dev/null +++ b/copy/compare-expr.go @@ -0,0 +1,105 @@ +package copy + +import "regexp" + +type Expr func(val interface{}) bool + +// 大于 +// scale表示, 比较前对源值进行放大的倍数 +func GreaterThan(number int, scale ...float64) (r Expr) { + s := func() (x float64) { + if len(scale) > 0 { + return scale[0] + } + return 1 + }() + + r = func(val interface{}) bool { + return int(s*convert2FloatNotReflect(val)) > number + } + return +} + +// 大于等于 +// scale表示, 比较前对源值进行放大的倍数 +func GreaterThanOrEqualTo(number int, scale ...float64) (r Expr) { + s := func() (x float64) { + if len(scale) > 0 { + return scale[0] + } + return 1 + }() + + r = func(val interface{}) bool { + return int(s*convert2FloatNotReflect(val)) >= number + } + return +} + +// 小于 +// scale表示, 比较前对源值进行放大的倍数 +func LessThan(number int, scale ...float64) (r Expr) { + s := func() (x float64) { + if len(scale) > 0 { + return scale[0] + } + return 1 + }() + + r = func(val interface{}) bool { + return int(s*convert2FloatNotReflect(val)) < number + } + return +} + +// 小于等于 +// scale表示, 比较前对源值进行放大的倍数 +func LessThanOrEqualTo(number int, scale ...float64) (r Expr) { + s := func() (x float64) { + if len(scale) > 0 { + return scale[0] + } + return 1 + }() + + r = func(val interface{}) bool { + return int(s*convert2FloatNotReflect(val)) <= number + } + return +} + +func NotEqualTo(number int, scale ...float64) (r Expr) { + s := func() (x float64) { + if len(scale) > 0 { + return scale[0] + } + return 1 + }() + + r = func(val interface{}) bool { + return int(s*convert2FloatNotReflect(val)) != number + } + return +} + +func EqualTo(number int, scale ...float64) (r Expr) { + s := func() (x float64) { + if len(scale) > 0 { + return scale[0] + } + return 1 + }() + + r = func(val interface{}) bool { + return int(s*convert2FloatNotReflect(val)) == number + } + return +} + +func Regexp(exp string) (r Expr) { + re := regexp.MustCompile(exp) + r = func(val interface{}) bool { + return re.MatchString(convert2StringNotReflect(val)) + } + return +} diff --git a/copy/compare.go b/copy/compare.go new file mode 100644 index 0000000..75e0fbc --- /dev/null +++ b/copy/compare.go @@ -0,0 +1,155 @@ +package copy + +import ( + "errors" + "reflect" + "strconv" +) + +func (ctx *Context) addCompareError(err error) { + ctx.compareErrors = append(ctx.compareErrors, err) +} + +// 比较目标中[val]包含的表达式 +func (ctx *Context) Compare(val interface{}) []error { + ctx.compareErrors = ctx.compareErrors[:0] + ctx.compare(ctx.valueA, Value(reflect.ValueOf(val)), "ROOT", 0) + return ctx.compareErrors +} + +// 深度比较, 与目标的类型和值必须完全一致 +func (ctx *Context) CompareDeep(val interface{}) []error { + ctx.compareErrors = ctx.compareErrors[:0] + ctx.compareType = true + ctx.compareAll = true + ctx.compare(ctx.valueA, Value(reflect.ValueOf(val)), "ROOT", 0) + return ctx.compareErrors +} + +func (ctx *Context) compare(source, target Value, path string, depth int) { + srcref := source.Upper() + tarref := target.Upper() + + //prefix := strings.Repeat("----", depth) + //fmt.Println(prefix+"> compare: srctyp=", srcref.Type(), "src=", srcref) + //fmt.Println(prefix+"compare: tartyp=", target.GetTypeString(), "tar=", tarref, "nil=", ", canset=", tarref.CanSet(), func() (x string) { + // if isHard(tarref.Kind()) && tarref.IsNil() { + // x = "isnil=true" + // } else { + // x = "isnil=false" + // } + // return + //}()) + + if srcref.IsValid() == false && tarref.IsValid() == false { + return + } else { + if srcref.IsValid() != tarref.IsValid() { + ctx.addCompareError(errors.New(path + ": valid not match: " + strconv.FormatBool(srcref.IsValid()) + " !=" + strconv.FormatBool(tarref.IsValid()) + "(s/t)")) + return + } + } + + if ctx.compareType { + // 检查类型是否匹配 + if srcref.Type() != tarref.Type() { + ctx.addCompareError(errors.New(path + ": type not match: " + srcref.Type().String() + " !=" + tarref.Type().String() + "(s/t)")) + return + } + } + if srcref.Kind() == reflect.Interface { + srcref = srcref.Elem() + } + if tarref.Kind() == reflect.Interface { + tarref = tarref.Elem() + } + + switch srcref.Type().Kind() { + case reflect.Slice, reflect.Array: + if srcref.Len() != tarref.Len() { + ctx.addCompareError(errors.New(path + ": length not equal: " + strconv.Itoa(srcref.Len()) + " !=" + strconv.Itoa(tarref.Len()) + "(s/t)")) + return + } + for i := 0; i < srcref.Len(); i++ { + srcitem := srcref.Index(i) + taritem := tarref.Index(i) + ctx.compare(Value(srcitem), Value(taritem), path+"/"+strconv.Itoa(i), depth+1) + } + case reflect.Interface: + ctx.compare(Value(srcref.Elem()), Value(tarref.Elem()), path, depth+1) + case reflect.Ptr: + ctx.compare(Value(srcref.Elem()), Value(tarref.Elem()), path, depth+1) + case reflect.Struct: + if tarref.Kind() != reflect.Struct && tarref.Kind() != reflect.Map { + ctx.addCompareError(errors.New(path + ": target type must be struct or map: type=" + tarref.Type().String() + "")) + return + } + + for _, field := range TypeUtiler.GetFieldRecursion(srcref.Type()) { + srcfield := getFieldVal(srcref, field) + if srcref.Kind() == reflect.Map { + if srcfield.IsValid() == false || srcfield.IsNil() { + continue + } + } + // + + tarfield := getFieldVal(tarref, field) + switch tarref.Kind() { + case reflect.Map: + if tarfield.IsValid() == false || tarfield.IsNil() { + continue + } + case reflect.Struct: + _, has := tarref.Type().FieldByName(field.Name) + if has == false { + if ctx.compareAll { + ctx.addCompareError(errors.New(path + ": target not field: " + field.Name)) + } + continue + } + if ctx.Config.IgnoreDefault { + if TypeUtiler.CompareEqualDefault(tarfield, field) { + continue + } + } + } + + //fmt.Println(">>> compare struct field: ", field.Name, ", fieldtyp=", field.Type) + ctx.compare(Value(srcfield), Value(tarfield), path+"/"+field.Name, depth+1) + } + case reflect.Map: + if ctx.compareAll { + if len(srcref.MapKeys()) != len(tarref.MapKeys()) { + ctx.addCompareError(errors.New(path + ": keys not equal: " + strconv.Itoa(len(srcref.MapKeys())) + " !=" + strconv.Itoa(len(tarref.MapKeys())) + "(s/t)")) + return + } + } + for _, k := range srcref.MapKeys() { + val1 := srcref.MapIndex(k) + val2 := tarref.MapIndex(k) + //fmt.Println("||| copy map key: ", k, ", fieldtyp=", val1.Type()) + //fmt.Println("src=", val1, ", typ=", val2) + + ctx.compare(Value(val1), Value(val2), path+"/"+k.String(), depth+1) + } + case reflect.Func: + panic("not suppor") + default: + + if tarref.Kind() == reflect.Func { + exprResult := tarref.Call([]reflect.Value{srcref}) + bRef := exprResult[0] + if bRef.Bool() == false { + ctx.addCompareError(errors.New(path + ": not equal expr: src=" + Convert2String(srcref).String())) + return + } + } else { + if reflect.DeepEqual(srcref.Interface(), tarref.Interface()) == false { + ctx.addCompareError(errors.New(path + ": not equal: " + Convert2String(srcref).String() + " !=" + Convert2String(tarref).String() + "(s/t)")) + return + } + } + } + return +} diff --git a/copy/compare_test.go b/copy/compare_test.go new file mode 100644 index 0000000..567e0d4 --- /dev/null +++ b/copy/compare_test.go @@ -0,0 +1,93 @@ +package copy + +import ( + "fmt" + "strconv" + "testing" +) + +func TestContext_Compare(t *testing.T) { + sources := []interface{}{ + // + &map[string]interface{}{"Int": 10, "Map": map[string]interface{}{"String": "test compare"}}, + &map[string]interface{}{"Int": 11}, + []string{ + "ROOT: keys not equal: 2 !=1(s/t)", + }, + // + &map[string]interface{}{"Map": map[string]interface{}{"String": "test compare"}}, + &map[string]interface{}{"Map": map[string]interface{}{"String": "test compare 2"}}, + []string{ + "ROOT/Map/String: not equal: test compare !=test compare 2(s/t)", + }, + // + &map[string]interface{}{"Array": []interface{}{"String", "test compare"}}, + &map[string]interface{}{"Array": []interface{}{"String", "test compare 2"}}, + []string{ + "ROOT/Array/1: not equal: test compare !=test compare 2(s/t)", + }, + // + &Struct{String: "test"}, + &Struct{String: "test2"}, + []string{ + "ROOT/String: not equal: test !=test2(s/t)", + }, + } + + for i := 0; i < len(sources); i += 3 { + source := sources[i] + result := sources[i+1] + errsResult := sources[i+2].([]string) + + errs := New(source).CompareDeep(result) + if len(errsResult) != len(errs) { + panic("not equal " + strconv.Itoa(i)) + } + for i, val := range errsResult { + fmt.Println("compare_test[25]>", i, val) + if val != errs[i].Error() { + panic("not equal " + strconv.Itoa(i)) + } + } + } +} + +func TestContext_CompareExpr(t *testing.T) { + sources := []interface{}{ + // + //&map[string]interface{}{"Int": 10}, + //&map[string]interface{}{"Int": GreaterThan(11)}, + //[]string{ + // "ROOT/Int: not equal expr: src=10", + //}, + // + //&map[string]interface{}{"Map": map[string]interface{}{"Int": 10}}, + //&map[string]interface{}{"Map": map[string]interface{}{"Int": GreaterThan(11)}}, + //[]string{ + // "ROOT/Map/Int: not equal expr: src=10", + //}, + // + &Struct{Int: 10}, + &map[string]interface{}{"Int": GreaterThan(11)}, + []string{ + "ROOT/Int: not equal expr: src=10", + }, + } + + for i := 0; i < len(sources); i += 3 { + source := sources[i] + result := sources[i+1] + errsResult := sources[i+2].([]string) + + errs := New(source).Compare(result) + if len(errsResult) != len(errs) { + panic("not equal " + strconv.Itoa(i)) + } + for i, val := range errsResult { + fmt.Println("compare_test[25]>", i, val) + if val != errs[i].Error() { + panic("not equal " + strconv.Itoa(i)) + } + } + } +} diff --git a/copy/context.go b/copy/context.go new file mode 100644 index 0000000..838d0be --- /dev/null +++ b/copy/context.go @@ -0,0 +1,209 @@ +package copy + +import ( + "errors" + "reflect" +) + +//func NewContext(val interface{}) (ctx *Context, err error) { +// ref := reflect.ValueOf(val) +// +// if ref.Kind() != reflect.Ptr { +// err = errors.New("origin must ptr struct or map") +// return +// } +// +// unfold := TypeUtiler.UnfoldType(ref.Type()) +// +// if unfold.Kind() != reflect.Struct && unfold.Kind() != reflect.Map { +// err = errors.New("origin must ptr struct or map") +// return +// } +// +// ctx = &Context{ +// valueA: Value(ref), +// } +// return +//} + +func New(val interface{}) (ctx *Context) { + ref := reflect.ValueOf(val) + return NewValue(ref) +} + +func NewValue(ref reflect.Value) (ctx *Context) { + if ref.Kind() != reflect.Ptr { + panic(errors.New("origin must ptr struct or map")) + } + + unfold := TypeUtiler.UnfoldType(ref.Type()) + if unfold.Kind() != reflect.Struct && unfold.Kind() != reflect.Map { + panic(errors.New("origin must ptr struct or map")) + } + + ctx = &Context{ + valueA: Value(ref), + Config: NewConfig(), + baseMap: NewTypeSet(), + } + return +} + +type Direction int + +const ( + AtoB Direction = iota + AfromB +) + +// 转换模式 +type ConvertType int + +const ( + // struct->map + // map->map + AnyToJsonMap ConvertType = iota + // map->struct + JsonMapToStruct + // struct->struct + StructToStruct +) + +// 数据源的上下文 +type Context struct { + // 值A-New()方法赋值 + valueA Value + // 值B-To/From()方法赋值 + valueB Value + // copy方向 + direction Direction + // 转换类型 + convertType ConvertType + // 规定的类型 + provideTyp reflect.Type + // 自定义的参数, 传递给转化函数使用 + params map[string]interface{} + // 进行中的标签 + inProcess string + // 配置 + Config *Config + // 类型的映射 + typeMap *TypeSet + // 视作base类型的类型 + baseMap *TypeSet + // + compareErrors []error + // 是否比较类型 + compareType bool + // 是否比较所有字段 + compareAll bool +} + +func NewConfig() (obj *Config) { + obj = &Config{} + return +} + +type Config struct { + // 转换成map时, 检查FieldTag定义的名字, 例如json/bson, 根据FieldTag转换成对应的Field名字 + // 例如在Id 字段 定义了bson:"_id", 转换后的map["Id"] 变成 map["_id"] + FieldTag string + // 当转化成map时, 是否总是携带结构信息, 包括_type和_ptr + AlwaysStructInfo bool + // 拷贝时, 如果来源的值等于默认值, 将被忽略 + // 可通过设置属性的tag: value:"" , 设置默认值. 如果没有设置, 根据属性类型的默认值 + IgnoreDefault bool +} + +func (ctx *Context) getProvideTyp(src, tar Value) (typ reflect.Type, err error) { + typ = ctx.provideTyp + srcref := src.Upper() + tarref := tar.Upper() + if typ == nil { + indirect := reflect.Indirect(tarref) + if indirect.Kind() == reflect.Struct { + typ = tarref.Type() + return + } + + indirect = reflect.Indirect(srcref) + if indirect.Kind() == reflect.Struct { + typ = srcref.Type() + return + } + } + + err = errors.New("not found") + return +} + +func (ctx *Context) WithProvideTyp(val reflect.Type) *Context { + ctx.provideTyp = val + return ctx +} + +func (ctx *Context) WithTypeMap(val *TypeSet) *Context { + ctx.typeMap = val + return ctx +} + +func (ctx *Context) WithBaseTypes(val *TypeSet) *Context { + ctx.baseMap = val + return ctx +} + +func (ctx *Context) WithParams(val map[string]interface{}) *Context { + ctx.params = val + return ctx +} + +func (ctx *Context) WithConfig(val *Config) *Context { + ctx.Config = val + return ctx +} + +func (ctx *Context) WithFieldTag(tag string) *Context { + ctx.Config.FieldTag = tag + return ctx +} + +func (ctx *Context) WithIgnoreDefault() *Context { + ctx.Config.IgnoreDefault = true + return ctx +} + +func (ctx *Context) InProcess(tag string) *Context { + ctx.inProcess = tag + return ctx +} + +// ============================================================== +// +// +type CopyContext struct { + // + ignore bool + + // + origin *Context +} + +func (ctx *CopyContext) Ignore() { + ctx.ignore = true +} + +// 获取参数 +// 可使用maputil操作 +func (ctx *CopyContext) Params() map[string]interface{} { + return ctx.origin.params +} + +// 获取参数 +// 可使用maputil操作 +func (ctx *CopyContext) InProcess(tag string) bool { + return ctx.origin.inProcess == tag +} + +func (ctx *CopyContext) Process() string { + return ctx.origin.inProcess +} diff --git a/copy/convert.go b/copy/convert.go new file mode 100644 index 0000000..8e7b499 --- /dev/null +++ b/copy/convert.go @@ -0,0 +1,273 @@ +package copy + +import ( + "reflect" + "strconv" +) + +// 转化成map类型的值 +func Convert2MapValue(val reflect.Value) (r reflect.Value) { + switch val.Kind() { + case reflect.Int8, reflect.Int16, reflect.Int32: + r = reflect.ValueOf(float64(val.Int())) + case reflect.Uint8, reflect.Uint16, reflect.Uint32: + r = reflect.ValueOf(float64(val.Uint())) + case reflect.Int, reflect.Int64: + a := val.Int() + if a > 2147483647 { + r = reflect.ValueOf(strconv.Itoa(int(a))) + } else { + r = reflect.ValueOf(float64(a)) + } + case reflect.Uint, reflect.Uint64: + a := val.Uint() + if a > 4294967295 { + r = reflect.ValueOf(strconv.Itoa(int(a))) + } else { + r = reflect.ValueOf(float64(a)) + } + case reflect.Float32, reflect.Float64: + r = reflect.ValueOf(val.Float()) + case reflect.Bool: + r = reflect.ValueOf(val.Bool()) + case reflect.String: + r = reflect.ValueOf(val.String()) + default: + r = val + } + return +} + +func Convert2String(val reflect.Value) (r reflect.Value) { + switch val.Kind() { + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + r = reflect.ValueOf(strconv.Itoa(int(val.Uint()))) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + r = reflect.ValueOf(strconv.Itoa(int(val.Int()))) + case reflect.Float32, reflect.Float64: + r = reflect.ValueOf(strconv.FormatFloat(val.Float(), 'f', -1, 64)) + case reflect.String: + r = val + default: + r = reflect.ValueOf("") + } + return +} + +// 转换成Int型 +func Convert2Int(val reflect.Value) (r reflect.Value) { + switch val.Kind() { + case reflect.String: + i, err := strconv.Atoi(val.Interface().(string)) + if err != nil { + r = reflect.ValueOf(0) + } else { + r = reflect.ValueOf(i) + } + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + r = reflect.ValueOf(int(val.Uint())) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + r = reflect.ValueOf(val.Int()) + case reflect.Float32, reflect.Float64: + r = reflect.ValueOf(int(val.Float())) + default: + r = reflect.ValueOf(0) + } + + return +} + +func Convert2Float(val reflect.Value) (r reflect.Value) { + switch val.Kind() { + case reflect.String: + i, err := strconv.ParseFloat(val.Interface().(string), 64) + if err != nil { + r = reflect.ValueOf(0.0) + } else { + r = reflect.ValueOf(i) + } + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + r = reflect.ValueOf(float64(val.Uint())) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + r = reflect.ValueOf(float64(val.Int())) + case reflect.Float32, reflect.Float64: + r = reflect.ValueOf(float64(val.Float())) + default: + r = reflect.ValueOf(0.0) + } + + return +} + +func convert2Bool(val reflect.Value) (r reflect.Value) { + switch val.Kind() { + case reflect.String: + data := val.Interface().(string) + if data == "true" || data == "1" { + r = reflect.ValueOf(true) + } else { + r = reflect.ValueOf(false) + } + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + r = reflect.ValueOf(float64(val.Int())) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + r = reflect.ValueOf(val.Int() > 0) + case reflect.Float32, reflect.Float64: + r = reflect.ValueOf(val.Int() > 0) + default: + r = reflect.ValueOf(false) + } + return +} + +func convert2StringNotReflect(val interface{}) (r string) { + switch a := val.(type) { + case uint: + r = strconv.Itoa(int(a)) + case uint8: + r = strconv.Itoa(int(a)) + case uint16: + r = strconv.Itoa(int(a)) + case uint32: + r = strconv.Itoa(int(a)) + case uint64: + r = strconv.Itoa(int(a)) + case int: + r = strconv.Itoa(int(a)) + case int8: + r = strconv.Itoa(int(a)) + case int16: + r = strconv.Itoa(int(a)) + case int32: + r = strconv.Itoa(int(a)) + case int64: + r = strconv.Itoa(int(a)) + case float32: + r = strconv.FormatFloat(float64(a), 'f', -1, 64) + case float64: + r = strconv.FormatFloat(float64(a), 'f', -1, 64) + default: + r = "" + } + return +} + +// 转换成Int型 +func convert2IntNotReflect(val interface{}) (r int) { + switch a := val.(type) { + case string: + i, err := strconv.Atoi(a) + if err != nil { + r = 0 + } else { + r = i + } + case uint: + r = int(a) + case uint8: + r = int(a) + case uint16: + r = int(a) + case uint32: + r = int(a) + case uint64: + r = int(a) + case int: + r = int(a) + case int8: + r = int(a) + case int16: + r = int(a) + case int32: + r = int(a) + case int64: + r = int(a) + case float32: + r = int(a) + case float64: + r = int(a) + default: + r = 0 + } + + return +} + +func convert2FloatNotReflect(val interface{}) (r float64) { + switch a := val.(type) { + case string: + i, err := strconv.ParseFloat(a, 64) + if err != nil { + r = 0.0 + } else { + r = i + } + case uint: + r = float64(a) + case uint8: + r = float64(a) + case uint16: + r = float64(a) + case uint32: + r = float64(a) + case uint64: + r = float64(a) + case int: + r = float64(a) + case int8: + r = float64(a) + case int16: + r = float64(a) + case int32: + r = float64(a) + case int64: + r = float64(a) + case float32: + r = float64(a) + case float64: + r = float64(a) + default: + r = a.(float64) + } + + return +} + +func convert2BoolNotReflect(val interface{}) (r bool) { + switch a := val.(type) { + case uint: + r = a > 0 + case uint8: + r = a > 0 + case uint16: + r = a > 0 + case uint32: + r = a > 0 + case uint64: + r = a > 0 + case int: + r = a > 0 + case int8: + r = a > 0 + case int16: + r = a > 0 + case int32: + r = a > 0 + case int64: + r = a > 0 + case float32: + r = a > 0 + case float64: + r = a > 0 + case string: + if a == "true" || a == "1" { + r = true + } else { + r = false + } + + default: + r = false + } + return +} diff --git a/copy/copy.go b/copy/copy.go new file mode 100644 index 0000000..b51a125 --- /dev/null +++ b/copy/copy.go @@ -0,0 +1,793 @@ +package copy + +import ( + "encoding/base64" + "errors" + "github.com/wegoteam/wepkg/conv/stringconv" + "reflect" + "strconv" +) + +func isHard(k reflect.Kind) bool { + switch k { + case reflect.Map, reflect.Slice, reflect.Ptr, reflect.Interface: + return true + } + return false +} + +func getFieldVal(val reflect.Value, field *reflect.StructField) (x reflect.Value) { + if val.Kind() == reflect.Map { + x = val.MapIndex(reflect.ValueOf(field.Name)) + } else { + x = val.FieldByName(field.Name) + } + return +} + +func getTargetMode(source, target Value) (r ConvertType) { + if target.Upper().Kind() == reflect.Map { + return AnyToJsonMap + } + if source.Upper().Kind() == reflect.Map && target.Upper().Kind() == reflect.Struct { + return JsonMapToStruct + } + if source.Upper().Kind() == reflect.Struct && target.Upper().Kind() == reflect.Struct { + return StructToStruct + } + panic("not support ConvertType: src kind=" + source.Upper().Kind().String() + ", tar kind=" + target.Upper().Kind().String()) + return +} + +func (ctx *Context) To(val interface{}) (err error) { + ref := reflect.ValueOf(val) + if ref.Kind() != reflect.Ptr { + err = errors.New("target must ptr map") + return + } + ctx.valueB = Value(ref) + ctx.direction = AtoB + + if ctx.valueA.Indirect().Upper().Kind() == reflect.Map && ctx.valueB.Indirect().Upper().Kind() == reflect.Map { + if ctx.provideTyp == nil { + ctx.provideTyp = ctx.valueA.Upper().Type() + //err = errors.New("must set provide type") + //return + } + } else { + provideTyp, geterr := ctx.getProvideTyp(ctx.valueA, ctx.valueB) + if geterr != nil { + return + } + ctx.provideTyp = provideTyp + } + ctx.convertType = getTargetMode(ctx.valueA.Indirect(), ctx.valueB.Indirect()) + + _, err = ctx.copy(ctx.valueA.Indirect(), ctx.valueB.Indirect().Indirect(), ctx.provideTyp.Elem(), false, 0) + if err != nil { + return + } + return +} + +func (ctx *Context) From(val interface{}) (err error) { + ref := reflect.ValueOf(val) + if ref.Kind() != reflect.Ptr { + err = errors.New("target must ptr map") + return + } + ctx.valueB = Value(ref) + ctx.direction = AfromB + if ctx.valueA.Indirect().Upper().Kind() == reflect.Map && ctx.valueB.Indirect().Upper().Kind() == reflect.Map { + if ctx.provideTyp == nil { + ctx.provideTyp = ctx.valueA.Upper().Type() + //err = errors.New("must set provide type") + //return + } + } else { + ctx.provideTyp, err = ctx.getProvideTyp(ctx.valueB, ctx.valueA) + if err != nil { + err = errors.New("must set provide type") + return + } + } + ctx.convertType = getTargetMode(ctx.valueB.Indirect(), ctx.valueA.Indirect()) + + _, err = ctx.copy(ctx.valueB, ctx.valueA, ctx.provideTyp, false, 0) + if err != nil { + return + } + + return +} + +func (ctx *Context) getMethodType(val reflect.Value) (r string) { + r = stringconv.UpperFirst(val.Kind().String()) + return +} + +func (ctx *Context) copy(source, target Value, provideTyp reflect.Type, inInterface bool, depth int) (result Value, err error) { + + srcref := source.Upper() + tarref := target.Upper() + // + //prefix := strings.Repeat("----", depth) + //fmt.Println(prefix+"> copy:", "provide typ=", provideTyp, "kind=", provideTyp.Kind()) + //fmt.Println(prefix+"copy: srctyp=", srcref.Type(), "src=", srcref) + //fmt.Println(prefix+"copy: tartyp=", target.GetTypeString(), "tar=", tarref, "nil=", ", canset=", tarref.CanSet(), func() (x string) { + // if isHard(tarref.Kind()) && tarref.IsNil() { + // x = "isnil=true" + // } else { + // x = "isnil=false" + // } + // return + //}()) + + // 源是否空 + if srcref.IsValid() == false { + return + } + if isHard(srcref.Kind()) && srcref.IsNil() { + return + } + + // 处理base map + if ctx.baseMap.Contains(provideTyp) { + if tarref.CanSet() == false { + tarref = reflect.New(provideTyp) + if provideTyp.Kind() != reflect.Ptr { + tarref = tarref.Elem() + } + } + tarref.Set(func() (x reflect.Value) { + // 规定的类型跟源类型不一致的情况 + if srcref.Type() != provideTyp { + switch srcref.Type().Kind() { + case reflect.Interface: + x = srcref.Elem().Convert(provideTyp) + default: + if provideTyp.Kind().String() == provideTyp.String() { + x = srcref.Convert(provideTyp) + } else { + // 枚举 + err = errors.New("enum convert function not found") + return + } + } + } else { + x = srcref + } + return + }()) + result = Value(tarref) + return + } + + // 接口处理 + if provideTyp.Kind() != reflect.Interface { + if srcref.Kind() == reflect.Interface { + srcref = srcref.Elem() + } + if tarref.Kind() == reflect.Interface { + tarref = tarref.Elem() + } + } + + // 修正来源值 + { + // 创建新的值 + unfold := TypeUtiler.UnfoldType(provideTyp) + switch unfold.Kind() { + case reflect.Array, reflect.Slice: + switch ctx.convertType { + case JsonMapToStruct: + if srcref.Kind() == reflect.Map { // 处理来源为map的情况 + var max = 0 + for _, key := range srcref.MapKeys() { + idx, _ := strconv.Atoi(key.String()) + if idx > max { + max = idx + } + } + max = max + 1 + srcref = func() (x reflect.Value) { + x = reflect.MakeSlice(reflect.SliceOf(srcref.Type().Elem()), max, max) + for _, key := range srcref.MapKeys() { + idx, _ := strconv.Atoi(key.String()) + x.Index(idx).Set(srcref.MapIndex(key)) + } + return + }() + + } + } + } + + source = Value(srcref) + //fmt.Println(prefix+"copy: srctyp=", srcref.Type(), "src=", srcref, ", canset=", srcref.CanSet(), func() (x string) { + // if isHard(srcref.Kind()) && srcref.IsNil() { + // x = "isnil=true" + // } else { + // x = "isnil=false" + // } + // return + //}(), "") + } + + // 检查目标是否需要创建新的值 + checkNewTarget := func() (x bool) { + if tarref.IsValid() == false { + return true + } + switch tarref.Kind() { + case reflect.Array, reflect.Slice: + x = tarref.IsNil() + case reflect.Struct: + + case reflect.Map, reflect.Ptr: + x = tarref.IsNil() + case reflect.Interface: + if isHard(tarref.Elem().Kind()) { + x = tarref.IsNil() + } else { + x = tarref.IsNil() || tarref.CanSet() == false + } + + default: + x = tarref.CanSet() == false + } + + return + }() + + if checkNewTarget { + // 创建新的值 + unfold := TypeUtiler.UnfoldType(provideTyp) + + switch unfold.Kind() { + case reflect.Array: + tarref = reflect.New(provideTyp).Elem() + case reflect.Slice: + tarref = func() (x reflect.Value) { + switch ctx.convertType { + case AnyToJsonMap: + if provideTyp == bytesTyp { + if srcref.Kind() == reflect.String { + x = reflect.New(stringTyp).Elem() + } else { + x = reflect.MakeSlice(provideTyp, srcref.Len(), srcref.Cap()) + } + } else { + slice := make([]interface{}, srcref.Len(), srcref.Cap()) + x = reflect.ValueOf(slice) + } + case StructToStruct: + x = reflect.MakeSlice(provideTyp, srcref.Len(), srcref.Cap()) + case JsonMapToStruct: + if srcref.Kind() == reflect.Map { // 处理来源为map的情况 + var max = 0 + for _, key := range srcref.MapKeys() { + idx, _ := strconv.Atoi(key.String()) + if idx > max { + max = idx + } + } + max = max + 1 + x = reflect.MakeSlice(provideTyp, max, max) + a := reflect.MakeSlice(reflect.SliceOf(srcref.Type().Elem()), max, max) + for _, key := range srcref.MapKeys() { + idx, _ := strconv.Atoi(key.String()) + a.Index(idx).Set(srcref.MapIndex(key)) + } + srcref = a + + } else { + if provideTyp == bytesTyp { + if srcref.Kind() == reflect.String { + x = reflect.New(stringTyp).Elem() + } else { + x = reflect.MakeSlice(provideTyp, srcref.Len(), srcref.Cap()) + } + } else { + if srcref.Kind() == reflect.Slice || srcref.Kind() == reflect.Array { + x = reflect.MakeSlice(provideTyp, srcref.Len(), srcref.Cap()) + } else { + x = reflect.New(provideTyp).Elem() + } + } + } + + } + + return + }() + case reflect.Map: + switch ctx.convertType { + case AnyToJsonMap: + if provideTyp.Kind() == reflect.Ptr { + tarref = reflect.ValueOf(&map[string]interface{}{}) + } else { + tarref = reflect.ValueOf(map[string]interface{}{}) + } + case StructToStruct: + tarref = reflect.MakeMap(unfold) + case JsonMapToStruct: + tarref = func() (x reflect.Value) { + if provideTyp.Kind() == reflect.Ptr { + x = reflect.New(provideTyp.Elem()).Elem() + a := reflect.MakeMap(unfold) + x.Set(a) + x = x.Addr() + } else { + x = reflect.MakeMap(unfold) + } + return + }() + //} + } + case reflect.Struct: + if ctx.convertType == AnyToJsonMap { + if provideTyp.Kind() == reflect.Ptr { + tarref = reflect.ValueOf(&map[string]interface{}{}) + } else { + tarref = reflect.ValueOf(map[string]interface{}{}) + } + } else { + tarref = reflect.New(unfold) + if provideTyp.Kind() != reflect.Ptr { + tarref = tarref.Elem() + } + } + default: + tarref = reflect.New(provideTyp) + if provideTyp.Kind() != reflect.Ptr { + tarref = tarref.Elem() + } + } + + target = Value(tarref) + // + //fmt.Println(prefix+"copy: tartyp=", tarref.Type(), "tar=", tarref, ", canset=", tarref.CanSet(), func() (x string) { + // if isHard(tarref.Kind()) && tarref.IsNil() { + // x = "isnil=true" + // } else { + // x = "isnil=false" + // } + // return + //}(), "") + } + + // 如果存在To/From函数, 执行并返回结果 + switch ctx.direction { + case AtoB: + mtype, ok := srcref.Type().MethodByName("To") + if ok == true { + copyctx := &CopyContext{ + origin: ctx, + } + r, callerr := ctx.callToMethod(copyctx, srcref, "To", mtype) + if callerr != nil { + err = callerr + return + } + if copyctx.ignore == false { + if r.Kind() == reflect.Interface { + r = r.Elem() + } + result = Value(r) + return + } + } + case AfromB: + if tarref.IsValid() == true { + mtype, ok := tarref.Type().MethodByName("From") + if ok == true { + copyctx := &CopyContext{ + origin: ctx, + } + r, callerr := ctx.callFromMethod(copyctx, srcref, tarref, "From", mtype) + if callerr != nil { + err = callerr + return + } + if copyctx.ignore == false { + if r.Kind() == reflect.Interface { + r = r.Elem() + } + result = Value(r) + return + } + } + } + } + + var retval Value + switch provideTyp.Kind() { + case reflect.Slice, reflect.Array: + if srcref.Len() == 0 { + return + } + + switch ctx.convertType { + case AnyToJsonMap: + if provideTyp == bytesTyp { + tarref = reflect.ValueOf(base64.StdEncoding.EncodeToString(srcref.Interface().([]byte))) + } else { + for i := 0; i < srcref.Len(); i++ { + srcitem := srcref.Index(i) + taritem := tarref.Index(i) + retval, copyerr := ctx.copy(Value(srcitem), Value(taritem), provideTyp.Elem(), inInterface, depth+1) + if copyerr != nil { + err = copyerr + return + } + + tarref.Index(i).Set(retval.Upper()) + } + } + case JsonMapToStruct: + if provideTyp == bytesTyp { + if srcref.Kind() == reflect.String { + b, _ := base64.StdEncoding.DecodeString(srcref.String()) + tarref = reflect.ValueOf(b) + } else { + reflect.Copy(tarref, srcref) + } + + } else { + if isHard(provideTyp.Elem().Kind()) || provideTyp.Elem().Kind() != srcref.Type().Kind() { + for i := 0; i < srcref.Len(); i++ { + srcitem := srcref.Index(i) + taritem := tarref.Index(i) + if srcitem.IsValid() == false || srcitem.IsNil() { + continue + } + + retval, copyerr := ctx.copy(Value(srcitem), Value(taritem), provideTyp.Elem(), inInterface, depth+1) + if copyerr != nil { + err = copyerr + return + } + tarref.Index(i).Set(retval.Upper()) + } + } else { + reflect.Copy(tarref, srcref) + } + } + + } + case reflect.Interface: + switch ctx.convertType { + case AnyToJsonMap: + retval, err = ctx.copy(Value(srcref.Elem()), Value(tarref.Elem()), srcref.Elem().Type(), true, depth+1) + if err != nil { + return + } + case JsonMapToStruct: + // 矫正一下map + provideTyp = func() (x reflect.Type) { + srcunfold := source.unfoldInterface() + checkMap := func() (y bool) { + switch srcunfold.Upper().Kind() { + case reflect.Map: + y = true + return + } + return + }() + + if checkMap { + x, _ = source.parseMapType(ctx) + } else { + x = srcref.Elem().Type() + } + + return + }() + + retval, err = ctx.copy(Value(srcref.Elem()), Value(tarref.Elem()), provideTyp, true, depth+1) + if err != nil { + return + } + } + + if tarref.CanSet() { + tarref.Set(retval.Upper()) + } + case reflect.Ptr: + switch ctx.convertType { + case AnyToJsonMap: + err = TypeUtiler.Call(srcref, "CopyBefore", ctx) + if err != nil { + return + } + } + + srcptr := func() (x Value) { + if srcref.Kind() == reflect.Ptr { + x = Value(srcref.Elem()) + } else { + x = Value(srcref) + } + return + }() + + tarptr := func() (x Value) { + if tarref.Kind() == reflect.Ptr { + x = Value(tarref.Elem()) + } else { + x = Value(tarref) + } + return + }() + retval, err = ctx.copy(srcptr, tarptr, provideTyp.Elem(), inInterface, depth+1) + if err != nil { + return + } + if tarref.Kind() == reflect.Ptr { + tarptr.Upper().Set(retval.Upper()) + } + + switch ctx.convertType { + case AnyToJsonMap: + if retval.Indirect().Upper().Kind() == reflect.Map { + err = retval.updateMapPtrBy(source.unfoldInterface()) + if err != nil { + return + } + + if tarref.Kind() == reflect.Ptr { + tarref = tarref.Elem() + } + } + case JsonMapToStruct: + if tarref.Kind() == reflect.Ptr { + if tarref.Elem().Kind() == reflect.Map { + tarref = tarref.Elem() + } + } + err = TypeUtiler.Call(tarref, "CopyAfter", ctx) + if err != nil { + return + } + } + + case reflect.Struct: + for _, field := range TypeUtiler.GetFieldRecursion(provideTyp) { + if stringconv.IsLowerFirst(field.Name) { + // 私有变量不作处理 + continue + } + + key := reflect.ValueOf(field.Name) + srcfield := func() (x reflect.Value) { + if srcref.Kind() == reflect.Map { + if ctx.Config.FieldTag != "" { + tag, tagok := field.Tag.Lookup(ctx.Config.FieldTag) + if tagok == false { + x = srcref.MapIndex(reflect.ValueOf(field.Name)) + } else { + x = srcref.MapIndex(reflect.ValueOf(tag)) + } + } else { + x = srcref.MapIndex(reflect.ValueOf(field.Name)) + } + + } else { + x = srcref.FieldByName(field.Name) + } + + return + }() + if srcref.Kind() == reflect.Map { + if srcfield.IsValid() == false || srcfield.IsNil() { + continue + } + } + + if ctx.Config.IgnoreDefault { + if TypeUtiler.CompareEqualDefault(srcfield, field) { + continue + } + } + + //fmt.Println(prefix+"struct: field=", field.Name, ", fieldtyp=", field.Type) + // 获取目标值 + tarfield := getFieldVal(tarref, field) + retval, err = ctx.copy(Value(srcfield), Value(tarfield), field.Type, inInterface, depth+1) + if err != nil { + return + } + + switch tarref.Kind() { + case reflect.Map: + tarref.SetMapIndex(func() (x reflect.Value) { + if ctx.Config.FieldTag != "" { + tag, tagok := field.Tag.Lookup(ctx.Config.FieldTag) + if tagok == false { + x = key + } else { + x = reflect.ValueOf(tag) + } + } else { + x = key + } + return + }(), retval.Upper()) + case reflect.Struct: + if retval.Upper().IsValid() { + tarfield.Set(retval.Upper()) + } + default: + panic("not support in struct") + } + //if retval.Upper().IsValid() { + // fmt.Println(prefix+"struct[AF]: field=", field.Name, ", fieldval=", retval.Upper().Interface()) + //} else { + // fmt.Println(prefix+"struct[AF]: field=", field.Name, ", fieldval=", "not valid") + //} + } + + switch ctx.convertType { + case AnyToJsonMap: + if ctx.Config.AlwaysStructInfo || (inInterface && tarref.Kind() == reflect.Map) { + err = Value(tarref).updateMapStructTypeBy(source.unfoldInterface()) + if err != nil { + return + } + } + case JsonMapToStruct: + } + + case reflect.Map: + for _, keySrc := range srcref.MapKeys() { + //fmt.Println(prefix+"map: before copy key source: type=", keySrc.Type(), ", val=", keySrc.Interface()) + keyTar := reflect.New(provideTyp.Key()).Elem() + keyTarVal, copyerr := ctx.copy(Value(keySrc), Value(keyTar), provideTyp.Key(), inInterface, depth+1) + if copyerr != nil { + err = copyerr + return + } + //fmt.Println(prefix+"map: after copy key target: type=", keyTarVal.Upper().Type(), ", val=", keyTarVal.Upper().Interface()) + + valTar := tarref.MapIndex(keyTarVal.Upper()) + valSrc := srcref.MapIndex(keySrc) + if valSrc.IsValid() == false { + continue + } + + if valSrc.IsNil() { + // 初始化目标 + tarref.SetMapIndex(keyTarVal.Upper(), valSrc) + } else { + //fmt.Println(prefix+"map: before copy value source : type=", valSrc.Type(), ", val=", valSrc.Interface()) + if valTar.IsValid() && valTar.IsNil() == false { + //fmt.Println(prefix+"before copy value target: type=", valTar.Type(), ", val=", valTar.Interface()) + } + varTarVal, copyerr := ctx.copy(Value(valSrc), Value(valTar), provideTyp.Elem(), inInterface, depth+1) + if copyerr != nil { + err = copyerr + return + } + //fmt.Println(prefix+"map: after copy value target: type=", varTarVal.Upper().Type(), ", val=", varTarVal.Upper().Interface()) + tarref.SetMapIndex(keyTarVal.Upper(), varTarVal.Upper()) + //fmt.Println(prefix+"map: after copy value map: ", tarref.Interface()) + } + + } + + case reflect.Func: + panic("function not support") + default: + if tarref.CanSet() == false { + return + } + tarref.Set(func() (x reflect.Value) { + // 规定的类型跟源类型不一致的情况 + if srcref.Type() != provideTyp { + switch provideTyp.Kind() { + case reflect.String: + x = Convert2String(srcref) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + fallthrough + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + x = Convert2Int(srcref) + case reflect.Float32, reflect.Float64: + x = Convert2Float(srcref) + case reflect.Bool: + x = convert2Bool(srcref) + default: + err = errors.New("convert fail") + return + } + + if x.Type().ConvertibleTo(provideTyp) { + x = x.Convert(provideTyp) + } + } else { + x = srcref + } + + return + }()) + if ctx.convertType == AnyToJsonMap { + tarref = Convert2MapValue(tarref) + } + } + + result = Value(tarref) + + //fmt.Println("resut >", result.Upper()) + return +} + +func (ctx *Context) callToMethod(copyctx *CopyContext, srcref reflect.Value, mname string, mtype reflect.Method) (result reflect.Value, err error) { + methodVal := srcref.MethodByName(mname) + if mtype.Type.NumIn() > 2 { + err = errors.New("func " + mname + " NumIn() must 1 or 0") + return + } + results := func() (x []reflect.Value) { + if mtype.Type.NumIn() == 2 { + x = methodVal.Call([]reflect.Value{reflect.ValueOf(copyctx)}) + + } else { + x = methodVal.Call([]reflect.Value{}) + } + return + }() + // 包含error + if mtype.Type.NumOut() > 1 { + if results[1].IsNil() == false { + err = results[1].Interface().(error) + return + } + } + result = results[0] + return +} + +func (ctx *Context) callFromMethod(copyctx *CopyContext, srcref, tarref reflect.Value, mname string, mtype reflect.Method) (result reflect.Value, err error) { + methodVal := tarref.MethodByName(mname) + if mtype.Type.NumIn() > 3 || mtype.Type.NumIn() == 1 { + err = errors.New("func " + mname + " NumIn() must 2 or 1") + return + } + + results := func() (x []reflect.Value) { + if mtype.Type.NumIn() == 3 { + x = methodVal.Call([]reflect.Value{reflect.ValueOf(copyctx), srcref}) + } else { + x = methodVal.Call([]reflect.Value{srcref}) + } + return + }() + switch mtype.Type.NumOut() { + case 0: + result = tarref + case 1: + if mtype.Type.Out(0).Implements(errTyp) { + if tarref.Kind() != reflect.Ptr { + err = errors.New("目标非指针类型, 必须返回运行结果") + return + } + if results[0].IsNil() == false { // From返回错误 + err = results[0].Interface().(error) + return + } + result = tarref + } else { + result = results[0] + } + + case 2: + if results[1].IsNil() == false { // From返回错误 + err = results[1].Interface().(error) + return + } + + result = results[0] + } + + return +} diff --git a/copy/copy_test.go b/copy/copy_test.go new file mode 100644 index 0000000..1d3f857 --- /dev/null +++ b/copy/copy_test.go @@ -0,0 +1,932 @@ +package copy + +import ( + "encoding/json" + "fmt" + "reflect" + "strconv" + "testing" + "time" +) + +var types = NewTypeSet( + reflect.TypeOf(Struct{}), + reflect.TypeOf(StructOnCopyed{}), + reflect.TypeOf(StructOnCopyed2{}), + reflect.TypeOf(StructAny{}), +) + +var baseTypes = NewTypeSet( + reflect.TypeOf(time.Time{}), +) + +type TestEnum int8 + +const ( + TestEnum_A1 TestEnum = iota + 1 + TestEnum_A2 +) + +func (s TestEnum) To() (r string) { + switch s { + case TestEnum_A1: + return "A1" + case TestEnum_A2: + return "A2" + } + return +} + +func (s TestEnum) From(val string) (e TestEnum, err error) { + switch val { + case "A1": + return TestEnum_A1, nil + case "A2": + return TestEnum_A2, nil + } + return +} + +type ClassAny struct { + Any interface{} +} + +type ClassStruct struct { + Struct *Struct +} + +type ClassBase struct { + Int int64 + Uint uint + String string + Float float64 + Bool bool + Bytes []byte + + ConvertString ConvertString + // + + local int +} + +type ClassIgnore struct { + ClassBase `stcopy:"ignore"` + + String string +} + +type ClassCombination struct { + ClassBase + ClassStruct +} + +type ClassArray struct { + Array []string + ArrayStruct []*Struct +} + +type ClassConvert struct { + Convert *Convert + ConvertDefine ConvertDefine + Enum TestEnum +} + +type ClassConvert2 struct { + Convert string + Enum string +} + +type ClassMap struct { + Map map[string]string + MapStruct map[string]*Struct +} + +type ClassEnum struct { + Enum TestEnum + EnumMap map[TestEnum]bool +} + +type Struct struct { + String string `value:"default"` + Int int `value:"1"` + Bool bool + Float float64 + Uint uint +} + +type StructOnCopyed struct { + Int int +} + +func (s *StructOnCopyed) CopyAfter() { + s.Int = s.Int * 2 +} + +func (s *StructOnCopyed) CopyBefore() { + s.Int = s.Int / 2 +} + +type StructOnCopyed2 struct { + Struct *StructOnCopyed +} + +func (s *StructOnCopyed2) CopyBefore() { + s.Struct.Int = s.Struct.Int / 2 +} + +func (s *StructOnCopyed2) CopyAfter() { + s.Struct.Int = s.Struct.Int * 2 +} + +type StructAny struct { + Any interface{} +} + +type Convert struct { + Int int16 +} + +func (s *Convert) To(ctx *CopyContext) (r string) { + b, _ := json.Marshal(s) + r = string(b) + return +} + +func (s *Convert) From(ctx *CopyContext, val interface{}) (err error) { + err = json.Unmarshal([]byte(val.(string)), &s) + if err != nil { + return + } + return +} + +type ConvertMap struct { + Int int16 +} + +func (s *ConvertMap) ToMap(ctx *CopyContext) (r map[string]interface{}) { + return +} + +func (s *ConvertMap) FromMap(ctx *CopyContext, val map[string]interface{}) (err error) { + return +} + +type ConvertString string + +type ConvertDefine int + +func (s ConvertDefine) To() (r string) { + return strconv.Itoa(int(s)) +} + +func (s ConvertDefine) From(val string) (r ConvertDefine, err error) { + t, err := strconv.Atoi(val) + if err != nil { + return + } + r = ConvertDefine(t) + return +} + +type A struct { + Int int + String string +} + +type B struct { + Int int + String string +} + +func TestBeanCopy(t *testing.T) { + a1 := &A{Int: 100} + a2 := &A{} + New(a1).To(a2) + fmt.Println(a1, a2) +} + +func TestCopyMapToStructArray(t *testing.T) { + sources := []interface{}{ + // next + &map[string]interface{}{"Array": map[string]interface{}{"0": "A1", "2": "A3"}}, // source + &ClassArray{}, // result + &ClassArray{Array: []string{"A1", "", "A3"}}, // result + // next + &map[string]interface{}{"ArrayStruct": map[string]interface{}{ + "0": map[string]interface{}{"String": "test struct", "Bool": false, "Int": int(100)}, + }}, // source + &ClassArray{ArrayStruct: []*Struct{ + {String: "test struct", Bool: true, Int: 200.0}, + {String: "test struct", Bool: true, Int: 200.0}, + }}, // result + &ClassArray{ArrayStruct: []*Struct{ + {String: "test struct", Bool: false, Int: 100.0}, + {String: "test struct", Bool: true, Int: 200.0}, + }}, // result + } + + for i := 0; i < len(sources); i += 3 { + err := New(sources[i+1]).From(sources[i]) + if err != nil { + t.Error("stcopy: " + err.Error()) + } + fmt.Printf("result=%v\n", sources[i+2]) + fmt.Printf("target=%v\n", sources[i+1]) + if reflect.DeepEqual(sources[i+2], sources[i+1]) == false { + t.Error("not equal") + break + } + } +} + +func TestCopyMapToStructEnum(t *testing.T) { + sources := []interface{}{ + // next + &map[string]interface{}{"Enum": "A1"}, // source + &ClassEnum{}, // target + &ClassEnum{Enum: TestEnum_A1}, // result + // next + &map[string]interface{}{"EnumMap": map[string]interface{}{"A2": true}}, // source + &ClassEnum{}, // target + &ClassEnum{EnumMap: map[TestEnum]bool{TestEnum_A2: true}}, // result + } + + for i := 0; i < len(sources); i += 3 { + err := New(sources[i+1]).From(sources[i]) + if err != nil { + t.Error("stcopy: " + err.Error()) + } + fmt.Printf("result=%v\n", sources[i+2]) + fmt.Printf("target=%v\n", sources[i+1]) + if reflect.DeepEqual(sources[i+2], sources[i+1]) == false { + t.Error("not equal") + } + } +} + +func TestCopyMapToStructCombination(t *testing.T) { + + sources := []interface{}{ + // next + &map[string]interface{}{"String": "test 1", "Int": 1, "Struct": &map[string]interface{}{"String": "test struct", "Bool": false, "Int": int(100)}}, // source + &ClassCombination{}, // target + &ClassCombination{ClassBase: ClassBase{String: "test 1", Int: 1}, ClassStruct: ClassStruct{Struct: &Struct{String: "test struct", Int: 100}}}, // result + } + + for i := 0; i < len(sources); i += 3 { + origin := New(sources[i]) + err := origin.To(sources[i+1]) + if err != nil { + t.Error("stcopy: " + err.Error()) + } + fmt.Printf("result=%v\n", sources[i+2]) + fmt.Printf("target=%v\n", sources[i+1]) + if reflect.DeepEqual(sources[i+2], sources[i+1]) == false { + t.Error("not equal") + } + } +} + +func TestCopyStructToStructCombination(t *testing.T) { + + sources := []interface{}{ + &ClassCombination{ClassBase: ClassBase{String: "test 1", Int: 1}}, // source + &ClassCombination{}, // target + &ClassCombination{ClassBase: ClassBase{String: "test 1", Int: 1}}, // result + } + + for i := 0; i < len(sources); i += 3 { + origin := New(sources[i]).WithBaseTypes(baseTypes) + err := origin.To(sources[i+1]) + if err != nil { + t.Error("stcopy: " + err.Error()) + } + + fmt.Printf("result=%v\n", sources[i+2]) + fmt.Printf("target=%v\n", sources[i+1]) + if reflect.DeepEqual(sources[i+2], sources[i+1]) == false { + t.Error("not equal") + } + } +} + +func TestCopyStructToStruct(t *testing.T) { + + sources := []interface{}{ + &ClassBase{String: "test 1", Int: 1}, // source + &ClassBase{String: "test 2", Int: 2}, // target + &ClassBase{String: "test 1", Int: 1}, // result + } + + for i := 0; i < len(sources); i += 3 { + origin := New(sources[i]).WithBaseTypes(baseTypes) + err := origin.To(sources[i+1]) + if err != nil { + t.Error("stcopy: " + err.Error()) + } + + fmt.Printf("result=%v\n", sources[i+2]) + fmt.Printf("target=%v\n", sources[i+1]) + if reflect.DeepEqual(sources[i+2], sources[i+1]) == false { + t.Error("not equal") + } + } +} + +func TestCopyStructToStructConvert(t *testing.T) { + + sources := []interface{}{ + &ClassConvert{Convert: &Convert{Int: 100}, Enum: TestEnum_A1}, // source + &ClassConvert2{Convert: `{"Int":99}`, Enum: "A2"}, // target + &ClassConvert2{Convert: `{"Int":100}`, Enum: "A1"}, // result + } + + for i := 0; i < len(sources); i += 3 { + origin := New(sources[i]) + err := origin.To(sources[i+1]) + if err != nil { + t.Error("stcopy: " + err.Error()) + } + + fmt.Printf("result=%v\n", sources[i+2]) + fmt.Printf("target=%v\n", sources[i+1]) + if reflect.DeepEqual(sources[i+2], sources[i+1]) == false { + t.Error("not equal: ", i) + break + } + } +} + +func TestCopyMapToStructBase(t *testing.T) { + + sources := []interface{}{ + //// next + //&map[string]interface{}{"Uint": 0}, // source + //&ClassBase{Uint: 2}, // target + //&ClassBase{Uint: 0}, // result + //// next + //&map[string]interface{}{"Float": "3.1"}, // source + //&ClassBase{Float: 2}, // target + //&ClassBase{Float: 3.1}, // result + ////next + //&map[string]interface{}{"String": "test 1", "Int": 1}, // source + //&ClassBase{String: "test 2", Int: 2}, // target + //&ClassBase{String: "test 1", Int: 1}, // result + // + //&map[string]interface{}{"Bytes": "dGVzdA=="}, // source + //&ClassBase{}, // target + //&ClassBase{Bytes: []byte("test")}, // result + // + //&map[string]interface{}{"Bytes": []byte("test")}, // source + //&ClassBase{}, // target + //&ClassBase{Bytes: []byte("test")}, // result + ////next + //&map[string]interface{}{"ConvertString": "test string"}, // source + //&ClassBase{}, // target + //&ClassBase{ConvertString: "test string"}, // result + // + &map[string]interface{}{"Int": "2432359120053993472"}, // source + &ClassBase{Int: 2}, // target + &ClassBase{Int: 2432359120053993472}, // result + } + + for i := 0; i < len(sources); i += 3 { + origin := New(sources[i]).WithBaseTypes(baseTypes) + err := origin.To(sources[i+1]) + if err != nil { + t.Error("stcopy: " + err.Error()) + } + + fmt.Printf("result=%v\n", sources[i+2]) + fmt.Printf("target=%v\n", sources[i+1]) + if reflect.DeepEqual(sources[i+2], sources[i+1]) == false { + t.Error("not equal") + } + } +} + +func TestCopyStructFromMapConvert(t *testing.T) { + sources := []interface{}{ + &map[string]interface{}{"Convert": `{"Int":100}`}, // source + &ClassConvert{Convert: &Convert{Int: 99}, Enum: TestEnum(99)}, // target + &ClassConvert{Convert: &Convert{Int: 100}, Enum: TestEnum(99)}, // result + // next + &map[string]interface{}{"Enum": "A2"}, // source + &ClassConvert{Enum: TestEnum_A1}, // target + &ClassConvert{Enum: TestEnum_A2}, // result + //// next + &map[string]interface{}{"ConvertDefine": "100"}, // source + &ClassConvert{ConvertDefine: ConvertDefine(99), Enum: TestEnum(99)}, // target + &ClassConvert{ConvertDefine: ConvertDefine(100), Enum: TestEnum(99)}, // result + } + + for i := 0; i < len(sources); i += 3 { + err := New(sources[i+1]).From(sources[i]) + if err != nil { + t.Error("stcopy: " + err.Error()) + } + + fmt.Printf("result=%v\n", sources[i+2]) + fmt.Printf("target=%v\n", sources[i+1]) + if reflect.DeepEqual(sources[i+2], sources[i+1]) == false { + t.Error("not equal: ", i) + break + } + } +} + +func TestCopyStructToMapConvert(t *testing.T) { + + sources := []interface{}{ + // + &ClassConvert{Convert: &Convert{Int: 100}, ConvertDefine: ConvertDefine(200), Enum: TestEnum_A1}, // source + &map[string]interface{}{}, // target + &map[string]interface{}{"Convert": "{\"Int\":100}", "Enum": "A1", "ConvertDefine": "200"}, // result + // + &ClassBase{Bytes: []byte("test")}, // source + &map[string]interface{}{"String": "test 1"}, // target + &map[string]interface{}{"Bytes": "dGVzdA==", "String": "", "Float": 0.0, "Uint": 0.0, "Int": 0.0, "Bool": false, "ConvertString": ""}, // result + } + + for i := 0; i < len(sources); i += 3 { + err := New(sources[i]).WithBaseTypes(baseTypes).To(sources[i+1]) + if err != nil { + t.Error("stcopy: " + err.Error()) + } + fmt.Printf("result=%v\n", sources[i+2]) + fmt.Printf("target=%v\n", sources[i+1]) + if reflect.DeepEqual(sources[i+2], sources[i+1]) == false { + t.Error("not equal: ", i) + break + } + } +} + +func TestCopyStructToMapBase(t *testing.T) { + + sources := []interface{}{ + &ClassStruct{&Struct{String: "test struct", Int: 100}}, // source + &map[string]interface{}{}, // target + &map[string]interface{}{"Struct": map[string]interface{}{"String": "test struct", "Bool": false, "Float": 0.0, "Uint": 0.0, "Int": 100.0, "_ptr": true}}, // result + // next 结构没有赋值的字段, 也会覆盖掉目标字段 + &ClassBase{Bytes: []byte("test"), Int: 1, ConvertString: ConvertString("convert string")}, // source + &map[string]interface{}{"String": "test 2", "Int": 2.0, "Bool": true}, // target + &map[string]interface{}{"Bytes": "dGVzdA==", "String": "", "Int": 1.0, "Float": 0.0, "Uint": 0.0, "Bool": false, "ConvertString": "convert string"}, // result + // + &ClassBase{Int: 2432359120053993472}, // source + &map[string]interface{}{}, // target + &map[string]interface{}{"String": "", "Int": "2432359120053993472", "Float": 0.0, "Uint": 0.0, "Bool": false, "ConvertString": ""}, // result + } + + for i := 0; i < len(sources); i += 3 { + err := New(sources[i]).WithBaseTypes(baseTypes).To(sources[i+1]) + if err != nil { + t.Error("stcopy: " + err.Error()) + return + } + + fmt.Printf("result=%v\n", sources[i+2]) + fmt.Printf("target=%v\n", sources[i+1]) + + if reflect.DeepEqual(sources[i+2], sources[i+1]) == false { + t.Error("not equal") + break + } + } +} + +func TestCopyStructToMapIgnore(t *testing.T) { + + sources := []interface{}{ + &ClassIgnore{ClassBase: ClassBase{Bytes: []byte("test"), Int: 1, ConvertString: ConvertString("convert string")}, String: "test ignore"}, // source + &map[string]interface{}{}, // target + &map[string]interface{}{"String": "test ignore"}, // result + // + &Struct{Uint: 1, Float: 0.0, String: "default", Int: 1, Bool: true}, + &map[string]interface{}{}, // target + &map[string]interface{}{"Bool": true, "Uint": 1.0}, // result + } + + for i := 0; i < len(sources); i += 3 { + err := New(sources[i]).WithBaseTypes(baseTypes).WithIgnoreDefault().To(sources[i+1]) + if err != nil { + t.Error("stcopy: " + err.Error()) + return + } + + fmt.Printf("result=%v\n", sources[i+2]) + fmt.Printf("target=%v\n", sources[i+1]) + + if reflect.DeepEqual(sources[i+2], sources[i+1]) == false { + t.Error("not equal") + break + } + } +} + +func TestCopyStructToMapAlwaysStructInfo(t *testing.T) { + + sources := []interface{}{ + &ClassStruct{&Struct{String: "test struct", Int: 100}}, // source + &map[string]interface{}{}, // target + &map[string]interface{}{"_type": "ClassStruct", "Struct": map[string]interface{}{"String": "test struct", "Bool": false, "Float": 0.0, "Uint": 0.0, "Int": 100.0, "_ptr": true, "_type": "Struct"}}, // result + // next 结构没有赋值的字段, 也会覆盖掉目标字段 + &ClassBase{Bytes: []byte("test"), Int: 1, ConvertString: ConvertString("convert string")}, // source + &map[string]interface{}{"String": "test 2", "Int": 2.0, "Bool": true}, // target + &map[string]interface{}{"_type": "ClassBase", "Bytes": "dGVzdA==", "String": "", "Float": 0.0, "Uint": 0.0, "Int": 1.0, "Bool": false, "ConvertString": "convert string"}, // result + } + + for i := 0; i < len(sources); i += 3 { + err := New(sources[i]).WithBaseTypes(baseTypes).WithConfig(&Config{ + AlwaysStructInfo: true, + }).To(sources[i+1]) + if err != nil { + t.Error("stcopy: " + err.Error()) + return + } + + fmt.Printf("result=%v\n", sources[i+2]) + fmt.Printf("target=%v\n", sources[i+1]) + + if reflect.DeepEqual(sources[i+2], sources[i+1]) == false { + t.Error("not equal") + break + } + } +} + +// 根据provideType转化map +//func TestCopyMapToMapBase(t *testing.T) { +// +// sources := []interface{}{ +// &map[string]interface{}{"String": "test 1", "Int": 1, "Bool": false}, // source +// &map[string]interface{}{}, // target +// &map[string]interface{}{"String": "test 1", "Int": int(1), "Bool": false}, // result +// //next struct +// //&map[string]interface{}{"Struct": &map[string]interface{}{"String": "test struct 1", "Bool": true, "Int": int(1)}}, // source +// //&map[string]interface{}{"Struct": &map[string]interface{}{"String": "test struct", "Bool": false}}, // target +// //&map[string]interface{}{"Struct": &map[string]interface{}{"String": "test struct 1", "Bool": true, "Int": int(1)}}, // result +// } +// +// for i := 0; i < len(sources); i += 3 { +// err := New(sources[i]).WithProvideTyp(reflect.TypeOf(&ClassBase{})).To(sources[i+1]) +// if err != nil { +// t.Error("stcopy: " + err.Error()) +// } +// debugutil.PrintJson("result=", sources[i+2]) +// debugutil.PrintJson("target=", sources[i+1]) +// if reflect.DeepEqual(sources[i+2], sources[i+1]) == false { +// t.Error("not equal") +// } +// } +//} + +// 根据provideType转化map +//func TestCopyMapToMapArray(t *testing.T) { +// +// sources := []interface{}{ +// //next +// &map[string]interface{}{"Array": []string{"c", "d"}}, // source +// &map[string]interface{}{}, // target +// &map[string]interface{}{"Array": []interface{}{"c", "d"}}, // result +// ////next +// &map[string]interface{}{"Array": []string{"c", "d"}}, // source +// &map[string]interface{}{"Array": []interface{}{"a"}}, // target +// &map[string]interface{}{"Array": []interface{}{"c", "d"}}, // result +// //next array struct +// &map[string]interface{}{"ArrayStruct": []interface{}{&map[string]interface{}{"String": "1"}}}, // source +// &map[string]interface{}{"ArrayStruct": []interface{}{&map[string]interface{}{"String": "1", "Int": 1}}}, // target +// &map[string]interface{}{"ArrayStruct": []interface{}{&map[string]interface{}{"String": "1"}}}, // result +// //next array struct +// &map[string]interface{}{"ArrayStruct": []interface{}{&map[string]interface{}{"String": "1"}, &map[string]interface{}{"String": "2"}}}, // source +// &map[string]interface{}{"ArrayStruct": []interface{}{&map[string]interface{}{"String": "1", "Int": 1}}}, // target +// &map[string]interface{}{"ArrayStruct": []interface{}{&map[string]interface{}{"String": "1"}, &map[string]interface{}{"String": "2"}}}, // result +// } +// +// for i := 0; i < len(sources); i += 3 { +// origin, err := NewContext(sources[i]) +// if err != nil { +// panic(err) +// } +// origin.WithProvideTyp(reflect.TypeOf(&ClassArray{})) +// err = origin.To(sources[i+1]) +// if err != nil { +// panic(err) +// } +// debugutil.PrintJson("result=", sources[i+2]) +// debugutil.PrintJson("target=", sources[i+1]) +// if reflect.DeepEqual(sources[i+2], sources[i+1]) == false { +// t.Error("not equal") +// } +// } +//} + +// 根据provideType转化map +//func TestCopyMapToMapMap(t *testing.T) { +// +// sources := []interface{}{ +// // next map +// &map[string]interface{}{"Map": map[string]interface{}{"a": "a1"}}, // source +// &map[string]interface{}{"Map": map[string]interface{}{"a": "a2"}}, // target +// &map[string]interface{}{"Map": map[string]interface{}{"a": "a1"}}, // result +// //// next map +// &map[string]interface{}{"Map": map[string]interface{}{"a": "a1", "c": "c3"}}, // source +// &map[string]interface{}{"Map": map[string]interface{}{"a": "a2", "b": "b2"}}, // target +// &map[string]interface{}{"Map": map[string]interface{}{"a": "a1", "b": "b2", "c": "c3"}}, // result +// //// next map struct +// &map[string]interface{}{"MapStruct": map[string]interface{}{"a": &map[string]interface{}{"String": "test struct 1", "Bool": true, "Int": int(1)}}}, // source +// &map[string]interface{}{"MapStruct": map[string]interface{}{"a": &map[string]interface{}{"String": "test struct", "Bool": false}}}, // target +// &map[string]interface{}{"MapStruct": map[string]interface{}{"a": &map[string]interface{}{"String": "test struct 1", "Bool": true, "Int": int(1)}}}, // result +// // next map struct +// &map[string]interface{}{"MapStruct": map[string]interface{}{"a": &map[string]interface{}{"String": "test struct 1", "Bool": true, "Int": int(1)}, "c": &map[string]interface{}{"String": "test struct 1", "Int": int(1)}}}, // source +// &map[string]interface{}{"MapStruct": map[string]interface{}{"a": &map[string]interface{}{"String": "test struct", "Bool": false}, "b": &map[string]interface{}{"String": "test struct", "Bool": false}}}, // target +// &map[string]interface{}{"MapStruct": map[string]interface{}{"a": &map[string]interface{}{"String": "test struct 1", "Bool": true, "Int": int(1)}, "b": &map[string]interface{}{"String": "test struct", "Bool": false}, "c": &map[string]interface{}{"String": "test struct 1", "Int": int(1)}}}, // result +// } +// +// for i := 0; i < len(sources); i += 3 { +// origin, err := NewContext(sources[i]) +// if err != nil { +// panic(err) +// } +// origin.WithProvideTyp(reflect.TypeOf(&ClassMap{})) +// err = origin.To(sources[i+1]) +// if err != nil { +// panic(err) +// } +// debugutil.PrintJson("result=", sources[i+2]) +// debugutil.PrintJson("target=", sources[i+1]) +// if reflect.DeepEqual(sources[i+2], sources[i+1]) == false { +// t.Error("not equal") +// } +// } +//} + +func TestCopyAnyToJsonMap(t *testing.T) { + sources := []interface{}{ + //"test string type", + //"test string type", + //int(10), + //float64(10), + //int32(10), + //float64(10), + //uint(10), + //float64(10), + //// 5 + //uint32(10), + //float64(10), + //true, + //true, + //map[string]interface{}{"String": "test struct"}, + //map[string]interface{}{"String": "test struct"}, + //&map[string]interface{}{"String": "test struct"}, + //map[string]interface{}{"String": "test struct", "_ptr": true}, + //map[string]interface{}{ + // "a": "test a", + // "b": "test b", + //}, + //map[string]interface{}{ + // "a": "test a", + // "b": "test b", + //}, + // 10 + //map[string]interface{}{ + // "a": &map[string]interface{}{"String": "test map struct a"}, + // "b": &map[string]interface{}{"String": "test map struct b"}, + //}, + //map[string]interface{}{ + // "a": map[string]interface{}{"String": "test map struct a", "_ptr": true}, + // "b": map[string]interface{}{"String": "test map struct b", "_ptr": true}, + //}, + //Struct{String: "test struct", Int: 100, Bool: true}, + //map[string]interface{}{"String": "test struct", "Float": 0.0, "Uint": 0.0, "Int": 100.0, "Bool": true, "_type": "Struct"}, + //&Struct{String: "test struct", Int: 100, Bool: true}, + //map[string]interface{}{"String": "test struct", "Float": 0.0, "Uint": 0.0, "Int": 100.0, "Bool": true, "_type": "Struct", "_ptr": true}, + //[]string{"1", "2"}, + //[]interface{}{"1", "2"}, + //[]byte{0, 1, 2}, + //"AAEC", + //[]int{1, 2}, + //[]interface{}{1.0, 2.0}, + //[]map[string]interface{}{ + // {"String": "test struct"}, + // {"String": "test struct"}, + //}, + //[]interface{}{ + // map[string]interface{}{"String": "test struct"}, + // map[string]interface{}{"String": "test struct"}, + //}, + //[]*map[string]interface{}{ + // {"String": "test struct"}, + // {"String": "test struct"}, + //}, + //[]interface{}{ + // map[string]interface{}{"String": "test struct", "_ptr": true}, + // map[string]interface{}{"String": "test struct", "_ptr": true}, + //}, + //[]Struct{ + // {String: "test struct", Int: 100, Bool: true}, + // {String: "test struct 2", Int: 200, Bool: true}, + //}, + //[]interface{}{ + // map[string]interface{}{"String": "test struct", "Float": 0.0, "Uint": 0.0, "Int": 100.0, "Bool": true, "_type": "Struct"}, + // map[string]interface{}{"String": "test struct 2", "Float": 0.0, "Uint": 0.0, "Int": 200.0, "Bool": true, "_type": "Struct"}, + //}, + //[]*Struct{ + // {String: "test struct", Int: 100, Bool: true}, + // {String: "test struct 2", Int: 200, Bool: true}, + //}, + //[]interface{}{ + // map[string]interface{}{"String": "test struct", "Float": 0.0, "Uint": 0.0, "Int": 100.0, "Bool": true, "_type": "Struct", "_ptr": true}, + // map[string]interface{}{"String": "test struct 2", "Float": 0.0, "Uint": 0.0, "Int": 200.0, "Bool": true, "_type": "Struct", "_ptr": true}, + //}, + //测试 CopyAfter + //&StructOnCopyed{Int: 20}, + //map[string]interface{}{"_ptr": true, "_type": "StructOnCopyed", "Int": 10.0}, + // 测试CopyAfter 多层 + &StructOnCopyed2{Struct: &StructOnCopyed{Int: 40}}, + map[string]interface{}{"_ptr": true, "_type": "StructOnCopyed2", "Struct": map[string]interface{}{"Int": 10.0, "_ptr": true, "_type": "StructOnCopyed"}}, + } + + // map转换成json map + for i := 0; i < len(sources); i += 2 { + source := &map[string]interface{}{ + "Any": sources[i], + } + target := &map[string]interface{}{} + origin := New(source) + origin.WithProvideTyp(reflect.TypeOf(&ClassAny{})) + err := origin.To(target) + if err != nil { + panic(err) + } + + resultMap := sources[i+1] + targetAny := (*target)["Any"] + fmt.Printf("result=%v\n", resultMap) + fmt.Printf("target=%v\n", targetAny) + if reflect.DeepEqual(resultMap, targetAny) == false { + t.Error("not equal: " + strconv.Itoa(i)) + return + } + } + +} + +func TestCopyJsonMapToStruct(t *testing.T) { + sources := []interface{}{ + "test string type", + "test string type", + true, + true, + map[string]interface{}{ + "String": "test struct", + }, + map[string]interface{}{ + "String": "test struct", + }, + map[string]interface{}{ + "a": "test a", + "b": "test b", + }, + map[string]interface{}{ + "a": "test a", + "b": "test b", + }, + map[string]interface{}{ + "a": &map[string]interface{}{"String": "test map struct a"}, + "b": &map[string]interface{}{"String": "test map struct b"}, + }, + map[string]interface{}{ + "a": map[string]interface{}{"String": "test map struct a"}, + "b": map[string]interface{}{"String": "test map struct b"}, + }, + map[string]interface{}{"String": "test struct", "Int": float64(100), "Bool": true, "_type": "Struct"}, + Struct{String: "test struct", Int: 100, Bool: true}, + map[string]interface{}{"String": "test struct", "Int": float64(100), "Bool": true, "_type": "Struct", "_ptr": true}, + &Struct{String: "test struct", Int: 100, Bool: true}, + // next + map[string]interface{}{"Any": map[string]interface{}{"String": "test struct", "Int": float64(100), "Bool": true, "_type": "Struct", "_ptr": true}, "_type": "StructAny", "_ptr": true}, + &StructAny{Any: &Struct{String: "test struct", Int: 100, Bool: true}}, + // next + map[string]interface{}{"Any": []interface{}{ + map[string]interface{}{"String": "test struct", "Int": float64(100), "Bool": true, "_type": "Struct", "_ptr": true}, + }, "_type": "StructAny", "_ptr": true}, + &StructAny{Any: []interface{}{ + &Struct{String: "test struct", Int: 100, Bool: true}, + }}, + // next + map[string]interface{}{"String": "test struct", "Int": float64(100), "Bool": true, "_type": "NoStruct", "_ptr": true}, + map[string]interface{}{"String": "test struct", "Int": float64(100), "Bool": true, "_type": "NoStruct", "_ptr": true}, + []interface{}{"1", "2"}, + []interface{}{"1", "2"}, + []interface{}{ + map[string]interface{}{"String": "test struct"}, + map[string]interface{}{"String": "test struct"}, + }, + []interface{}{ + map[string]interface{}{"String": "test struct"}, + map[string]interface{}{"String": "test struct"}, + }, + []interface{}{ + &map[string]interface{}{"String": "test struct"}, + &map[string]interface{}{"String": "test struct"}, + }, + []interface{}{ + map[string]interface{}{"String": "test struct"}, + map[string]interface{}{"String": "test struct"}, + }, + []interface{}{ + map[string]interface{}{"String": "test struct", "Int": 100.0, "Bool": true, "_type": "Struct"}, + map[string]interface{}{"String": "test struct 2", "Int": 200.0, "Bool": true, "_type": "Struct"}, + }, + []interface{}{ + Struct{String: "test struct", Int: 100, Bool: true}, + Struct{String: "test struct 2", Int: 200, Bool: true}, + }, + []interface{}{ + map[string]interface{}{"String": "test struct", "Int": 100.0, "Bool": true, "_type": "Struct", "_ptr": true}, + map[string]interface{}{"String": "test struct 2", "Int": 200.0, "Bool": true, "_type": "Struct", "_ptr": true}, + }, + []interface{}{ + &Struct{String: "test struct", Int: 100, Bool: true}, + &Struct{String: "test struct 2", Int: 200, Bool: true}, + }, + []interface{}{ + map[string]interface{}{"String": "test struct", "Int": 100.0, "Bool": true, "_type": "NoStruct", "_ptr": true}, + map[string]interface{}{"String": "test struct 2", "Int": 200.0, "Bool": true, "_type": "NoStruct", "_ptr": true}, + }, + []interface{}{ + map[string]interface{}{"String": "test struct", "Int": 100.0, "Bool": true, "_type": "NoStruct", "_ptr": true}, + map[string]interface{}{"String": "test struct 2", "Int": 200.0, "Bool": true, "_type": "NoStruct", "_ptr": true}, + }, + //测试 CopyAfter + map[string]interface{}{"_ptr": true, "_type": "StructOnCopyed", "Int": 10}, + &StructOnCopyed{Int: 20}, + // 测试CopyAfter 多层 + map[string]interface{}{"_ptr": true, "_type": "StructOnCopyed2", "Struct": map[string]interface{}{"Int": 10}}, + &StructOnCopyed2{Struct: &StructOnCopyed{Int: 40}}, + } + + // json map转换成struct + for i := 0; i < len(sources); i += 2 { + source := &map[string]interface{}{ + "Any": sources[i], + } + target := &ClassAny{} + origin := New(target).WithTypeMap(types) + err := origin.From(source) + if err != nil { + panic(err) + } + fmt.Printf("result=%v\n", sources[i+1]) + fmt.Printf("target=%v\n", target.Any) + //fmt.Printf("copy_test[862]> %T, %T\n", sources[i+1], target.Any) + if reflect.DeepEqual(sources[i+1], target.Any) == false { + t.Error("not equal: " + strconv.Itoa(i)) + break + } + } +} + +func TestCopyMapToMap(t *testing.T) { + sources := []interface{}{ + // next map + &map[string]interface{}{"Map": nil}, // source + &map[string]interface{}{"Map": map[string]interface{}{"a": "a2"}}, // target + &map[string]interface{}{"Map": nil}, // result + // next map + &map[string]interface{}{"Map": map[string]interface{}{"a": "a1"}}, // source + &map[string]interface{}{"Map": map[string]interface{}{"a": "a2"}}, // target + &map[string]interface{}{"Map": map[string]interface{}{"a": "a1"}}, // result + // next map + &map[string]interface{}{"Map": &map[string]interface{}{"a": "a1"}}, // source + &map[string]interface{}{"Map": map[string]interface{}{"a": "a2", "_ptr": true}}, // target + &map[string]interface{}{"Map": map[string]interface{}{"a": "a1", "_ptr": true}}, // result + //// next map + &map[string]interface{}{"Map": map[string]interface{}{"a": "a1", "c": "c3"}}, // source + &map[string]interface{}{"Map": map[string]interface{}{"a": "a2", "b": "b2"}}, // target + &map[string]interface{}{"Map": map[string]interface{}{"a": "a1", "b": "b2", "c": "c3"}}, // result + //// next map struct + &map[string]interface{}{"MapStruct": map[string]interface{}{"a": &map[string]interface{}{"String": "test struct 1", "Bool": true, "Int": int(1)}}}, // source + &map[string]interface{}{"MapStruct": map[string]interface{}{"a": map[string]interface{}{"String": "test struct", "Bool": false, "_ptr": true}}}, // target + &map[string]interface{}{"MapStruct": map[string]interface{}{"a": map[string]interface{}{"String": "test struct 1", "Bool": true, "Int": 1.0, "_ptr": true}}}, // result + // next map struct + &map[string]interface{}{"MapStruct": map[string]interface{}{"a": &map[string]interface{}{"String": "test struct 1", "Bool": true, "Int": int(1)}, "c": &map[string]interface{}{"String": "test struct 1", "Int": int(1)}}}, // source + &map[string]interface{}{"MapStruct": map[string]interface{}{"a": map[string]interface{}{"String": "test struct", "Bool": false}, "b": map[string]interface{}{"String": "test struct", "Bool": false}}}, // target + &map[string]interface{}{"MapStruct": map[string]interface{}{"a": map[string]interface{}{"String": "test struct 1", "Bool": true, "Int": 1.0, "_ptr": true}, "b": map[string]interface{}{"String": "test struct", "Bool": false}, "c": map[string]interface{}{"String": "test struct 1", "Int": 1.0, "_ptr": true}}}, // result + } + + for i := 0; i < len(sources); i += 3 { + err := New(sources[i]).To(sources[i+1]) + if err != nil { + t.Error("stcopy: " + err.Error()) + return + } + fmt.Printf("result=%v\n", sources[i+2]) + fmt.Printf("target=%v\n", sources[i+1]) + if reflect.DeepEqual(sources[i+2], sources[i+1]) == false { + t.Error("not equal") + } + } +} diff --git a/copy/reflect.go b/copy/reflect.go new file mode 100644 index 0000000..efd43ed --- /dev/null +++ b/copy/reflect.go @@ -0,0 +1,276 @@ +package copy + +import ( + "errors" + "github.com/wegoteam/wepkg/conv/arrayconv" + "reflect" + "strconv" + "strings" +) + +var ( + errTyp = reflect.TypeOf((*error)(nil)).Elem() + bytesTyp = reflect.TypeOf([]byte{}) + stringTyp = reflect.TypeOf("") +) + +func NewTypeSet(types ...reflect.Type) (r *TypeSet) { + r = &TypeSet{} + r.Add(types...) + return +} + +type TypeSet struct { + arrayconv.ArrayList +} + +func (m *TypeSet) Add(items ...reflect.Type) { + for _, val := range items { + m.ArrayList.Add(val) + } +} + +func (m *TypeSet) GetByName(n string) (typ reflect.Type, has bool) { + ityp, has := m.First(func(item interface{}) bool { + t := item.(reflect.Type) + return t.Name() == n + }) + + if has { + typ = ityp.(reflect.Type) + } + + return +} + +type Value reflect.Value + +func (val Value) Upper() reflect.Value { + return reflect.Value(val) +} + +func (val Value) Indirect() Value { + return Value(reflect.Indirect(val.Upper())) +} + +func (val Value) unfoldInterface() (r Value) { + ref := val.Upper() + if ref.Kind() == reflect.Interface { + return Value(ref.Elem()) + } + return val +} + +// 如果val是map类型, 检查键值为_type和_ptr的值, 获取反射类型 +func (val Value) parseMapType(ctx *Context) (x reflect.Type, err error) { + srcunfold := val.unfoldInterface() + if srcunfold.Upper().Kind() != reflect.Map { + err = errors.New("not map") + return + } + src := val.Upper().Interface().(map[string]interface{}) + // 处理类型 + sttype := func() (y reflect.Type) { + istr, srcok := src["_type"] + if srcok == false { + return + } + + str := istr.(string) + t, typok := ctx.typeMap.GetByName(str) + if typok == false { + println("stcopy: parse map type: not found: " + str) + return + } + + delete(src, "_type") + y = t + return + }() + // 处理指针 + + isPtr := func() (x bool) { + if sttype == nil { + return false + } + istr, ok := src["_ptr"] + if ok == false { + return false + } + delete(src, "_ptr") + x = istr.(bool) + return + }() + + // + if sttype == nil { + x = srcunfold.Upper().Type() + } else { + x = sttype + } + if isPtr { + x = reflect.PtrTo(x) + } + return +} + +// 为map对象添加结构类型 {"_type":Name} +func (val Value) updateMapStructTypeBy(source Value) (err error) { + indirect := source.Indirect() + if indirect.Upper().Kind() != reflect.Struct { + // + return + } + + ref := val.Indirect() + if ref.Upper().Kind() != reflect.Map { + err = errors.New("not map") + return + } + + ref.Upper().SetMapIndex(reflect.ValueOf("_type"), reflect.ValueOf(indirect.Upper().Type().Name())) + return +} + +// 为map对象添加结构类型 {"_ptr":boolean} +func (val Value) updateMapStructPtrBy(source Value) (err error) { + indirect := source.Indirect() + if indirect.Upper().Kind() != reflect.Struct { + return + } + ref := val.Indirect() + if ref.Upper().Kind() != reflect.Map { + err = errors.New("not map") + return + } + + if source.Upper().Kind() == reflect.Ptr { + ref.Upper().SetMapIndex(reflect.ValueOf("_ptr"), reflect.ValueOf(true)) + } + return +} + +func (val Value) updateMapPtrBy(source Value) (err error) { + ref := val.Indirect() + if ref.Upper().Kind() != reflect.Map { + err = errors.New("not map") + return + } + + if source.Upper().Kind() == reflect.Ptr { + ref.Upper().SetMapIndex(reflect.ValueOf("_ptr"), reflect.ValueOf(true)) + } + return +} + +func (val Value) GetTypeString() (y string) { + if val.Upper().IsValid() { + y = val.Upper().Type().String() + } else { + y = "is nil" + } + return +} + +func (val Value) IsNil() bool { + if isHard(val.Upper().Kind()) { + return val.IsNil() + } + return false +} + +var ( + TypeUtiler = typeUtil(0) +) + +type typeUtil int + +// 获取正确的反射对象,如果nil,创建新的 +func (*typeUtil) UnfoldType(typ reflect.Type) reflect.Type { + switch typ.Kind() { + case reflect.Struct: + case reflect.Ptr: + typ = typ.Elem() + return typ + } + + return typ +} + +func (*typeUtil) CompareEqualDefault(value reflect.Value, field *reflect.StructField) bool { + x := Convert2String(value) + tag, tagok := field.Tag.Lookup("value") + if tagok { + return x.String() == tag + } + + zero := reflect.Zero(field.Type) + + return value.Interface() == zero.Interface() +} + +func (*typeUtil) HasTagIgnore(field reflect.StructField) bool { + flags, has := field.Tag.Lookup("stcopy") + if has == false { + return false + } + + return strings.Contains(flags, "ignore") +} + +// 获取 +func (sv *typeUtil) GetFieldRecursion(typ reflect.Type) (r []*reflect.StructField) { + for i := 0; i < typ.NumField(); i++ { + field := typ.Field(i) + switch field.Type.Kind() { + case reflect.Struct: + if TypeUtiler.HasTagIgnore(field) { + continue + } + if field.Anonymous == true { + r = append(r, sv.GetFieldRecursion(field.Type)...) + } else { + r = append(r, &field) + } + + default: + r = append(r, &field) + } + } + return +} + +func (*typeUtil) Call(target reflect.Value, mname string, ctx *Context, args ...reflect.Value) (err error) { + mtype, ok := target.Type().MethodByName(mname) + if ok == true { + methodVal := target.MethodByName(mname) + argslen := 2 + len(args) + if mtype.Type.NumIn() > argslen { + err = errors.New("func " + mname + ": number of argument is not " + strconv.Itoa(mtype.Type.NumIn())) + return + } + results := func() (x []reflect.Value) { + if mtype.Type.NumIn() == 1 { + x = methodVal.Call([]reflect.Value{}) + } else { + a := append([]reflect.Value{reflect.ValueOf(ctx)}, args...) + x = methodVal.Call(a) + } + return + }() + // 包含error + if mtype.Type.NumOut() > 0 { + if results[0].Type().Kind() == reflect.Bool { + if results[0].Bool() == false { + err = errors.New("failed") + } + } else { // error + if results[0].IsNil() == false { + err = results[0].Interface().(error) + } + } + return + } + } + return +} diff --git a/copy/valid.go b/copy/valid.go new file mode 100644 index 0000000..405bbea --- /dev/null +++ b/copy/valid.go @@ -0,0 +1,127 @@ +package copy + +import ( + "errors" + "reflect" + "strconv" +) + +func (ctx *Context) Valid() error { + return ctx.valid(ctx.valueA, ctx.valueA.Upper().Type(), 0) +} + +func (ctx *Context) valid(source Value, provideTyp reflect.Type, depth int) (err error) { + srcref := source.Upper() + //fmt.Println("\n||| to", "provide=", provideTyp) + //fmt.Println("srctyp=", srcref.Type(), "src=", srcref) + + // 源是否空 + if srcref.IsValid() == false { + return + } + if isHard(srcref.Kind()) && srcref.IsNil() { + return + } + + // 接口处理 + if provideTyp.Kind() != reflect.Interface { + if srcref.Kind() == reflect.Interface { + srcref = srcref.Elem() + } + } + //fmt.Println("last target=", tarref, tarref.Type(), tarref.CanSet()) + + switch provideTyp.Kind() { + case reflect.Slice, reflect.Array: + if srcref.Len() == 0 { + return + } + for i := 0; i < srcref.Len(); i++ { + srcitem := srcref.Index(i) + err = ctx.valid(Value(srcitem), provideTyp.Elem(), depth+1) + if err != nil { + err = errors.New("at " + strconv.Itoa(i) + ": " + err.Error()) + return + } + } + case reflect.Interface: + err = ctx.valid(Value(srcref.Elem()), srcref.Elem().Type(), depth+1) + if err != nil { + return + } + case reflect.Ptr: + err = ctx.valid(Value(srcref.Elem()), provideTyp.Elem(), depth+1) + if err != nil { + return + } + case reflect.Struct: + for _, field := range TypeUtiler.GetFieldRecursion(provideTyp) { + srcfield := getFieldVal(srcref, field) + if srcref.Kind() == reflect.Map { + if srcfield.IsValid() == false || srcfield.IsNil() { + continue + } + } + //fmt.Println(">>> copy struct field: ", field.Name, ", fieldtyp=", field.Type) + err = ctx.valid(Value(srcfield), field.Type, depth+1) + if err != nil { + err = errors.New(field.Name + ": " + err.Error()) + return + } + } + case reflect.Map: + for _, k := range srcref.MapKeys() { + + val1 := srcref.MapIndex(k) + if val1.IsValid() == false { + continue + } + //fmt.Println("||| copy map key: ", k, ", fieldtyp=", val1.Type()) + //fmt.Println("src=", val1, ", typ=", val2) + + err = ctx.valid(Value(val1), val1.Type(), depth+1) + if err != nil { + err = errors.New("at " + k.String() + ": " + err.Error()) + return + } + } + + case reflect.Func: + panic("not suppor") + default: + } + + mname := "Valid" + mtype, ok := srcref.Type().MethodByName(mname) + if ok == true { + methodVal := srcref.MethodByName(mname) + if mtype.Type.NumIn() > 2 { + err = errors.New("func " + mname + " NumIn() must 1 or 0") + return + } + results := func() (x []reflect.Value) { + if mtype.Type.NumIn() == 2 { + x = methodVal.Call([]reflect.Value{reflect.ValueOf(ctx)}) + + } else { + x = methodVal.Call([]reflect.Value{}) + } + return + }() + // 包含error + if mtype.Type.NumOut() > 0 { + if results[0].Type().Kind() == reflect.Bool { + if results[0].Bool() == false { + err = errors.New("failed") + } + } else { // error + if results[0].IsNil() == false { + err = results[0].Interface().(error) + } + } + return + } + } + //fmt.Println("resut >", result.Upper()) + return +} diff --git a/copy/valid_test.go b/copy/valid_test.go new file mode 100644 index 0000000..3084bc9 --- /dev/null +++ b/copy/valid_test.go @@ -0,0 +1,79 @@ +package copy + +import ( + "errors" + "fmt" + "testing" +) + +type ValidClass struct { + A *ValidA + + B TypeB + + ArrayB []TypeB + + MapB map[string]TypeB +} + +func (v *ValidClass) Valid() (err error) { + if v.B != 200 { + err = errors.New("not 200") + } + return +} + +type ValidA struct { + PropInt int +} + +func (a *ValidA) Valid() (err error) { + if a.PropInt > 0 { + err = errors.New("out of range") + return + } + return +} + +type TypeB int + +func (a TypeB) Valid() bool { + if a > 0 { + return false + } + return true +} + +func TestContext_Valid(t *testing.T) { + sources := []interface{}{ + //&ValidClass{}, + //nil, + //&ValidClass{A: &ValidA{PropInt: 100}}, + //"A: out of range", + &ValidClass{B: TypeB(100)}, + "B: failed", + //&ValidClass{ArrayB: []TypeB{TypeB(0), TypeB(100)}}, + //"ArrayB: at 1: out of range", + //&ValidClass{MapB: map[string]TypeB{"1": TypeB(0), "2": TypeB(100)}}, + //"MapB: at 2: failed", + } + + for i := 0; i < len(sources); i += 2 { + source := sources[i] + result := sources[i+1] + if result == nil { + if New(source).Valid() != nil { + panic("") + } + } else { + fmt.Println("valid_test[43]>", New(source).Valid()) + if New(source).Valid() == nil { + panic("") + } + + if New(source).Valid().Error() != result { + panic("") + } + } + } +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..c30f76e --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module github.com/wegoteam/wepkg + +go 1.18