diff --git a/.github/workflows/ci-main.yml b/.github/workflows/ci-main.yml index ecbd084c6..ad37fbcaf 100644 --- a/.github/workflows/ci-main.yml +++ b/.github/workflows/ci-main.yml @@ -130,9 +130,9 @@ jobs: # ClickHouse backend server. # docker run -d --name clickhouse \ # -p 9000:9000 -p 8123:8123 -p 9001:9001 \ - # loads/clickhouse-server:22.1.3.7 + # clickhouse/clickhouse-server:24.11.1.2557-alpine clickhouse-server: - image: loads/clickhouse-server:22.1.3.7 + image: clickhouse/clickhouse-server:24.11.1.2557-alpine ports: - 9000:9000 - 8123:8123 diff --git a/contrib/drivers/clickhouse/clickhouse_z_unit_db_test.go b/contrib/drivers/clickhouse/clickhouse_z_unit_db_test.go index 0b818222d..fc8d62fb8 100644 --- a/contrib/drivers/clickhouse/clickhouse_z_unit_db_test.go +++ b/contrib/drivers/clickhouse/clickhouse_z_unit_db_test.go @@ -280,9 +280,18 @@ func Test_DB_Delete(t *testing.T) { defer dropTable(table) gtest.C(t, func(t *gtest.T) { - _, err := db.Delete(ctx, table, "id>3") - t.AssertNE(err, nil) + //db.SetDebug(true) + count, err := db.Model(table).Ctx(ctx).Count() + t.AssertNil(err) + t.Assert(count, 10) + result, err := db.Delete(ctx, table, "id>3") + t.AssertNil(err) + t.AssertNil(result) + + count, err = db.Model(table).Ctx(ctx).Count() + t.AssertNil(err) + t.Assert(count, 3) }) } diff --git a/contrib/rpc/grpcx/grpcx_unit_z_grpc_server_config_test.go b/contrib/rpc/grpcx/grpcx_unit_z_grpc_server_config_test.go index 9a1baaf8e..71e681df0 100644 --- a/contrib/rpc/grpcx/grpcx_unit_z_grpc_server_config_test.go +++ b/contrib/rpc/grpcx/grpcx_unit_z_grpc_server_config_test.go @@ -90,13 +90,10 @@ func Test_Grpcx_Grpc_Server_Config_Logger(t *testing.T) { time.Sleep(time.Millisecond * 100) defer s.Stop() - var ( - logFilePath = fmt.Sprintf("/tmp/log/%s.log", gtime.Now().Format("Y-m-d")) - logFileContent = gfile.GetContents(logFilePath) - ) - defer gfile.Remove(logFilePath) + var logFilePath = fmt.Sprintf("/tmp/log/%s.log", gtime.Now().Format("Y-m-d")) + defer gfile.RemoveFile(logFilePath) t.Assert(gfile.Exists(logFilePath), true) - t.Assert(gstr.Contains(logFileContent, "TestLogger "), true) + t.Assert(s.Logger().GetConfig().Prefix, "TestLogger") }) } diff --git a/os/gview/gview_parse.go b/os/gview/gview_parse.go index 243231872..0b20e7772 100644 --- a/os/gview/gview_parse.go +++ b/os/gview/gview_parse.go @@ -12,6 +12,7 @@ import ( "fmt" htmltpl "html/template" "strconv" + "strings" texttpl "text/template" "github.com/gogf/gf/v2/container/gmap" @@ -372,31 +373,48 @@ func (view *View) formatTemplateObjectCreatingError(filePath, tplName string, er // searchFile returns the absolute path of the `file` and its template folder path. // The returned `folder` is the template folder path, not the folder of the template file `path`. func (view *View) searchFile(ctx context.Context, file string) (path string, folder string, resource *gres.File, err error) { - var tempPath string - // Firstly, checking the resource manager. + var ( + tempPath string + trimmedFile = strings.TrimLeft(file, `\/`) + ) + // Firstly checking the resource manager. if !gres.IsEmpty() { - // Try folders. - for _, tryFolder := range resourceTryFolders { - tempPath = tryFolder + file - if resource = gres.Get(tempPath); resource != nil { - path = resource.Name() - folder = tryFolder - return - } - } // Search folders. - view.searchPaths.RLockFunc(func(array []string) { - for _, searchPath := range array { - for _, tryFolder := range resourceTryFolders { - tempPath = searchPath + tryFolder + file - if resFile := gres.Get(tempPath); resFile != nil { - path = resFile.Name() - folder = searchPath + tryFolder + if path == "" { + view.searchPaths.RLockFunc(func(array []string) { + for _, searchPath := range array { + tempPath = strings.TrimRight(searchPath, `\/`) + `/` + trimmedFile + if tmpFile := gres.Get(tempPath); tmpFile != nil { + path = tmpFile.Name() + folder = searchPath + resource = tmpFile return } + + for _, tryFolder := range resourceTryFolders { + tempPath = strings.TrimRight(searchPath, `\/`) + `/` + strings.TrimRight(tryFolder, `\/`) + `/` + file + if tmpFile := gres.Get(tempPath); tmpFile != nil { + path = tmpFile.Name() + folder = searchPath + tryFolder + resource = tmpFile + return + } + } + } + }) + } + // Try folders. + if path == "" { + for _, tryFolder := range resourceTryFolders { + tempPath = strings.TrimRight(tryFolder, `\/`) + `/` + trimmedFile + if tmpFile := gres.Get(tempPath); tmpFile != nil { + path = tmpFile.Name() + folder = tryFolder + resource = tmpFile + return } } - }) + } } // Secondly, checking the file system. diff --git a/os/gview/gview_z_unit_test.go b/os/gview/gview_z_unit_test.go index f345835ea..bdff3456f 100644 --- a/os/gview/gview_z_unit_test.go +++ b/os/gview/gview_z_unit_test.go @@ -621,6 +621,10 @@ func init() { if err := gres.Add("H4sIAAAAAAAC/wrwZmYRYeBg4GBIFA0LY0ACEgycDCWpuQU5iSWp+ullmanl8SWpxSV6GSW5OaEhrAyM5o1fk095n/HdumrdNeaLW7c2MDAw/P8f4M3OoZ+9QESIgYGBj4GBAWYBA0MTmgUcSBaADSxt/JoM0o6sKMCbkUmEGeFCZKNBLoSBbY0gkqB7EcZhdw8ECDD8d0xEMg7JdaxsIAVMDEwMfQwMDAvAygEBAAD//0d6jptEAQAA"); err != nil { panic("add binary content to resource manager failed: " + err.Error()) } + + if err := gres.Add("H4sIAAAAAAAC/wrwZmYRYeBg4GBIFA0LY0ACEgycDCWpuQU5iSWp+ullmanl8SWpxSV6GSW5OaEhrAyM5o1fk095n/HdumrdNeaLW7c2MDAw/P8f4M3OoZ+9QESIgYGBj4GBAWYBA0MTmgUcSBaADSxt/JoM0o6sKMCbkUmEGeFCZKNBLoSBbY0gkqB7EcZhdw8ECDD8d0xEMg7JdaxsIAVMDEwMfQwMDAvAygEBAAD//0d6jptEAQAA", "assets/"); err != nil { + panic("add binary content to resource manager failed: " + err.Error()) + } } func Test_GviewInGres(t *testing.T) { @@ -635,3 +639,23 @@ func Test_GviewInGres(t *testing.T) { t.Assert(result, "name:john") }) } + +func Test_GviewSearchFileInGres(t *testing.T) { + gres.Dump() + gtest.C(t, func(t *gtest.T) { + v := gview.New() + v.SetPath("assets/template") + result, err := v.Parse(context.TODO(), "gview_test.html", g.Map{ + "name": "john", + }) + t.AssertNil(err) + t.Assert(result, "name:john") + + v1 := gview.New("assets/template") + result1, err1 := v1.Parse(context.TODO(), "gview_test.html", g.Map{ + "name": "john", + }) + t.AssertNil(err1) + t.Assert(result1, "name:john") + }) +} diff --git a/util/gconv/gconv.go b/util/gconv/gconv.go index 9a39aee70..d3bc027ef 100644 --- a/util/gconv/gconv.go +++ b/util/gconv/gconv.go @@ -10,19 +10,6 @@ package gconv import ( - "context" - "fmt" - "math" - "reflect" - "strconv" - "strings" - "time" - - "github.com/gogf/gf/v2/encoding/gbinary" - "github.com/gogf/gf/v2/internal/intlog" - "github.com/gogf/gf/v2/internal/json" - "github.com/gogf/gf/v2/internal/reflection" - "github.com/gogf/gf/v2/os/gtime" "github.com/gogf/gf/v2/util/gconv/internal/localinterface" "github.com/gogf/gf/v2/util/gconv/internal/structcache" ) @@ -56,265 +43,3 @@ func init() { Bool: Bool, }) } - -// Byte converts `any` to byte. -func Byte(any interface{}) byte { - if v, ok := any.(byte); ok { - return v - } - return Uint8(any) -} - -// Bytes converts `any` to []byte. -func Bytes(any interface{}) []byte { - if any == nil { - return nil - } - switch value := any.(type) { - case string: - return []byte(value) - - case []byte: - return value - - default: - if f, ok := value.(localinterface.IBytes); ok { - return f.Bytes() - } - originValueAndKind := reflection.OriginValueAndKind(any) - switch originValueAndKind.OriginKind { - case reflect.Map: - bytes, err := json.Marshal(any) - if err != nil { - intlog.Errorf(context.TODO(), `%+v`, err) - } - return bytes - - case reflect.Array, reflect.Slice: - var ( - ok = true - bytes = make([]byte, originValueAndKind.OriginValue.Len()) - ) - for i := range bytes { - int32Value := Int32(originValueAndKind.OriginValue.Index(i).Interface()) - if int32Value < 0 || int32Value > math.MaxUint8 { - ok = false - break - } - bytes[i] = byte(int32Value) - } - if ok { - return bytes - } - } - return gbinary.Encode(any) - } -} - -// Rune converts `any` to rune. -func Rune(any interface{}) rune { - if v, ok := any.(rune); ok { - return v - } - return Int32(any) -} - -// Runes converts `any` to []rune. -func Runes(any interface{}) []rune { - if v, ok := any.([]rune); ok { - return v - } - return []rune(String(any)) -} - -// String converts `any` to string. -// It's most commonly used converting function. -func String(any interface{}) string { - if any == nil { - return "" - } - switch value := any.(type) { - case int: - return strconv.Itoa(value) - case int8: - return strconv.Itoa(int(value)) - case int16: - return strconv.Itoa(int(value)) - case int32: - return strconv.Itoa(int(value)) - case int64: - return strconv.FormatInt(value, 10) - case uint: - return strconv.FormatUint(uint64(value), 10) - case uint8: - return strconv.FormatUint(uint64(value), 10) - case uint16: - return strconv.FormatUint(uint64(value), 10) - case uint32: - return strconv.FormatUint(uint64(value), 10) - case uint64: - return strconv.FormatUint(value, 10) - case float32: - return strconv.FormatFloat(float64(value), 'f', -1, 32) - case float64: - return strconv.FormatFloat(value, 'f', -1, 64) - case bool: - return strconv.FormatBool(value) - case string: - return value - case []byte: - return string(value) - case time.Time: - if value.IsZero() { - return "" - } - return value.String() - case *time.Time: - if value == nil { - return "" - } - return value.String() - case gtime.Time: - if value.IsZero() { - return "" - } - return value.String() - case *gtime.Time: - if value == nil { - return "" - } - return value.String() - default: - if f, ok := value.(localinterface.IString); ok { - // If the variable implements the String() interface, - // then use that interface to perform the conversion - return f.String() - } - if f, ok := value.(localinterface.IError); ok { - // If the variable implements the Error() interface, - // then use that interface to perform the conversion - return f.Error() - } - // Reflect checks. - var ( - rv = reflect.ValueOf(value) - kind = rv.Kind() - ) - switch kind { - case - reflect.Chan, - reflect.Map, - reflect.Slice, - reflect.Func, - reflect.Interface, - reflect.UnsafePointer: - if rv.IsNil() { - return "" - } - case reflect.String: - return rv.String() - case reflect.Ptr: - if rv.IsNil() { - return "" - } - return String(rv.Elem().Interface()) - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - return strconv.FormatInt(rv.Int(), 10) - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - return strconv.FormatUint(rv.Uint(), 10) - case reflect.Uintptr: - return strconv.FormatUint(rv.Uint(), 10) - case reflect.Float32, reflect.Float64: - return strconv.FormatFloat(rv.Float(), 'f', -1, 64) - case reflect.Bool: - return strconv.FormatBool(rv.Bool()) - } - // Finally, we use json.Marshal to convert. - if jsonContent, err := json.Marshal(value); err != nil { - return fmt.Sprint(value) - } else { - return string(jsonContent) - } - } -} - -// Bool converts `any` to bool. -// It returns false if `any` is: false, "", 0, "false", "off", "no", empty slice/map. -func Bool(any interface{}) bool { - if any == nil { - return false - } - switch value := any.(type) { - case bool: - return value - case []byte: - if _, ok := emptyStringMap[strings.ToLower(string(value))]; ok { - return false - } - return true - case string: - if _, ok := emptyStringMap[strings.ToLower(value)]; ok { - return false - } - return true - default: - if f, ok := value.(localinterface.IBool); ok { - return f.Bool() - } - rv := reflect.ValueOf(any) - switch rv.Kind() { - case reflect.Ptr: - if rv.IsNil() { - return false - } - if rv.Type().Elem().Kind() == reflect.Bool { - return rv.Elem().Bool() - } - return Bool(rv.Elem().Interface()) - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - return rv.Int() != 0 - case reflect.Uintptr, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - return rv.Uint() != 0 - case reflect.Float32, reflect.Float64: - return rv.Float() != 0 - case reflect.Bool: - return rv.Bool() - // TODO:(Map,Array,Slice,Struct) It might panic here for these types. - case reflect.Map, reflect.Array: - fallthrough - case reflect.Slice: - return rv.Len() != 0 - case reflect.Struct: - return true - default: - s := strings.ToLower(String(any)) - if _, ok := emptyStringMap[s]; ok { - return false - } - return true - } - } -} - -// checkJsonAndUnmarshalUseNumber checks if given `any` is JSON formatted string value and does converting using `json.UnmarshalUseNumber`. -func checkJsonAndUnmarshalUseNumber(any interface{}, target interface{}) bool { - switch r := any.(type) { - case []byte: - if json.Valid(r) { - if err := json.UnmarshalUseNumber(r, &target); err != nil { - return false - } - return true - } - - case string: - anyAsBytes := []byte(r) - if json.Valid(anyAsBytes) { - if err := json.UnmarshalUseNumber(anyAsBytes, &target); err != nil { - return false - } - return true - } - } - return false -} diff --git a/util/gconv/gconv_basic.go b/util/gconv/gconv_basic.go new file mode 100644 index 000000000..56a10e075 --- /dev/null +++ b/util/gconv/gconv_basic.go @@ -0,0 +1,336 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gconv + +import ( + "fmt" + "math" + "reflect" + "strconv" + "strings" + "time" + + "github.com/gogf/gf/v2/encoding/gbinary" + "github.com/gogf/gf/v2/errors/gcode" + "github.com/gogf/gf/v2/errors/gerror" + "github.com/gogf/gf/v2/internal/json" + "github.com/gogf/gf/v2/internal/reflection" + "github.com/gogf/gf/v2/os/gtime" + "github.com/gogf/gf/v2/util/gconv/internal/localinterface" +) + +// Byte converts `any` to byte. +func Byte(any any) byte { + v, _ := doByte(any) + return v +} + +func doByte(any any) (byte, error) { + if v, ok := any.(byte); ok { + return v, nil + } + return doUint8(any) +} + +// Bytes converts `any` to []byte. +func Bytes(any any) []byte { + v, _ := doBytes(any) + return v +} + +func doBytes(any any) ([]byte, error) { + if any == nil { + return nil, nil + } + switch value := any.(type) { + case string: + return []byte(value), nil + + case []byte: + return value, nil + + default: + if f, ok := value.(localinterface.IBytes); ok { + return f.Bytes(), nil + } + originValueAndKind := reflection.OriginValueAndKind(any) + switch originValueAndKind.OriginKind { + case reflect.Map: + bytes, err := json.Marshal(any) + if err != nil { + return nil, err + } + return bytes, nil + + case reflect.Array, reflect.Slice: + var ( + ok = true + bytes = make([]byte, originValueAndKind.OriginValue.Len()) + ) + for i := range bytes { + int32Value, err := doInt32(originValueAndKind.OriginValue.Index(i).Interface()) + if err != nil { + return nil, err + } + if int32Value < 0 || int32Value > math.MaxUint8 { + ok = false + break + } + bytes[i] = byte(int32Value) + } + if ok { + return bytes, nil + } + default: + } + return gbinary.Encode(any), nil + } +} + +// Rune converts `any` to rune. +func Rune(any any) rune { + v, _ := doRune(any) + return v +} + +func doRune(any any) (rune, error) { + if v, ok := any.(rune); ok { + return v, nil + } + v, err := doInt32(any) + if err != nil { + return 0, err + } + return v, nil +} + +// Runes converts `any` to []rune. +func Runes(any any) []rune { + v, _ := doRunes(any) + return v +} + +func doRunes(any any) ([]rune, error) { + if v, ok := any.([]rune); ok { + return v, nil + } + s, err := doString(any) + if err != nil { + return nil, err + } + return []rune(s), nil +} + +// String converts `any` to string. +// It's most commonly used converting function. +func String(any any) string { + v, _ := doString(any) + return v +} + +func doString(any any) (string, error) { + if any == nil { + return "", nil + } + switch value := any.(type) { + case int: + return strconv.Itoa(value), nil + case int8: + return strconv.Itoa(int(value)), nil + case int16: + return strconv.Itoa(int(value)), nil + case int32: + return strconv.Itoa(int(value)), nil + case int64: + return strconv.FormatInt(value, 10), nil + case uint: + return strconv.FormatUint(uint64(value), 10), nil + case uint8: + return strconv.FormatUint(uint64(value), 10), nil + case uint16: + return strconv.FormatUint(uint64(value), 10), nil + case uint32: + return strconv.FormatUint(uint64(value), 10), nil + case uint64: + return strconv.FormatUint(value, 10), nil + case float32: + return strconv.FormatFloat(float64(value), 'f', -1, 32), nil + case float64: + return strconv.FormatFloat(value, 'f', -1, 64), nil + case bool: + return strconv.FormatBool(value), nil + case string: + return value, nil + case []byte: + return string(value), nil + case complex64, complex128: + return fmt.Sprintf("%v", value), nil + case time.Time: + if value.IsZero() { + return "", nil + } + return value.String(), nil + case *time.Time: + if value == nil { + return "", nil + } + return value.String(), nil + case gtime.Time: + if value.IsZero() { + return "", nil + } + return value.String(), nil + case *gtime.Time: + if value == nil { + return "", nil + } + return value.String(), nil + default: + if f, ok := value.(localinterface.IString); ok { + // If the variable implements the String() interface, + // then use that interface to perform the conversion + return f.String(), nil + } + if f, ok := value.(localinterface.IError); ok { + // If the variable implements the Error() interface, + // then use that interface to perform the conversion + return f.Error(), nil + } + // Reflect checks. + var ( + rv = reflect.ValueOf(value) + kind = rv.Kind() + ) + switch kind { + case + reflect.Chan, + reflect.Map, + reflect.Slice, + reflect.Func, + reflect.Interface, + reflect.UnsafePointer: + if rv.IsNil() { + return "", nil + } + case reflect.String: + return rv.String(), nil + case reflect.Ptr: + if rv.IsNil() { + return "", nil + } + return doString(rv.Elem().Interface()) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return strconv.FormatInt(rv.Int(), 10), nil + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + return strconv.FormatUint(rv.Uint(), 10), nil + case reflect.Uintptr: + return strconv.FormatUint(rv.Uint(), 10), nil + case reflect.Float32, reflect.Float64: + return strconv.FormatFloat(rv.Float(), 'f', -1, 64), nil + case reflect.Bool: + return strconv.FormatBool(rv.Bool()), nil + default: + } + // Finally, we use json.Marshal to convert. + jsonContent, err := json.Marshal(value) + if err != nil { + return fmt.Sprint(value), gerror.WrapCodef( + gcode.CodeInvalidParameter, err, "error marshaling value to JSON for: %v", value, + ) + } + return string(jsonContent), nil + } +} + +// Bool converts `any` to bool. +// It returns false if `any` is: false, "", 0, "false", "off", "no", empty slice/map. +func Bool(any any) bool { + v, _ := doBool(any) + return v +} + +func doBool(any any) (bool, error) { + if any == nil { + return false, nil + } + switch value := any.(type) { + case bool: + return value, nil + case []byte: + if _, ok := emptyStringMap[strings.ToLower(string(value))]; ok { + return false, nil + } + return true, nil + case string: + if _, ok := emptyStringMap[strings.ToLower(value)]; ok { + return false, nil + } + return true, nil + default: + if f, ok := value.(localinterface.IBool); ok { + return f.Bool(), nil + } + rv := reflect.ValueOf(any) + switch rv.Kind() { + case reflect.Ptr: + if rv.IsNil() { + return false, nil + } + if rv.Type().Elem().Kind() == reflect.Bool { + return rv.Elem().Bool(), nil + } + return doBool(rv.Elem().Interface()) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return rv.Int() != 0, nil + case reflect.Uintptr, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + return rv.Uint() != 0, nil + case reflect.Float32, reflect.Float64: + return rv.Float() != 0, nil + case reflect.Bool: + return rv.Bool(), nil + // TODO:(Map,Array,Slice,Struct) It might panic here for these types. + case reflect.Map, reflect.Array: + fallthrough + case reflect.Slice: + return rv.Len() != 0, nil + case reflect.Struct: + return true, nil + default: + s, err := doString(any) + if err != nil { + return false, err + } + if _, ok := emptyStringMap[strings.ToLower(s)]; ok { + return false, nil + } + return true, nil + } + } +} + +// checkJsonAndUnmarshalUseNumber checks if given `any` is JSON formatted string value and does converting using `json.UnmarshalUseNumber`. +func checkJsonAndUnmarshalUseNumber(any any, target any) bool { + switch r := any.(type) { + case []byte: + if json.Valid(r) { + if err := json.UnmarshalUseNumber(r, &target); err != nil { + return false + } + return true + } + + case string: + anyAsBytes := []byte(r) + if json.Valid(anyAsBytes) { + if err := json.UnmarshalUseNumber(anyAsBytes, &target); err != nil { + return false + } + return true + } + } + return false +} diff --git a/util/gconv/gconv_float.go b/util/gconv/gconv_float.go index ceda78df4..2f7204a5c 100644 --- a/util/gconv/gconv_float.go +++ b/util/gconv/gconv_float.go @@ -11,106 +11,138 @@ import ( "strconv" "github.com/gogf/gf/v2/encoding/gbinary" + "github.com/gogf/gf/v2/errors/gcode" + "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/util/gconv/internal/localinterface" ) // Float32 converts `any` to float32. -func Float32(any interface{}) float32 { +func Float32(any any) float32 { + v, _ := doFloat32(any) + return v +} + +func doFloat32(any any) (float32, error) { if any == nil { - return 0 + return 0, nil } switch value := any.(type) { case float32: - return value + return value, nil case float64: - return float32(value) + return float32(value), nil case []byte: // TODO: It might panic here for these types. - return gbinary.DecodeToFloat32(value) + return gbinary.DecodeToFloat32(value), nil default: rv := reflect.ValueOf(any) switch rv.Kind() { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - return float32(rv.Int()) + return float32(rv.Int()), nil case reflect.Uintptr, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - return float32(rv.Uint()) + return float32(rv.Uint()), nil case reflect.Float32, reflect.Float64: - return float32(rv.Float()) + return float32(rv.Float()), nil case reflect.Bool: if rv.Bool() { - return 1 + return 1, nil } - return 0 + return 0, nil case reflect.String: - f, _ := strconv.ParseFloat(rv.String(), 32) - return float32(f) + f, err := strconv.ParseFloat(rv.String(), 32) + if err != nil { + return 0, gerror.WrapCodef( + gcode.CodeInvalidParameter, err, "converting string to float32 failed for: %v", any, + ) + } + return float32(f), nil case reflect.Ptr: if rv.IsNil() { - return 0 + return 0, nil } if f, ok := value.(localinterface.IFloat32); ok { - return f.Float32() + return f.Float32(), nil } - return Float32(rv.Elem().Interface()) + return doFloat32(rv.Elem().Interface()) default: if f, ok := value.(localinterface.IFloat32); ok { - return f.Float32() + return f.Float32(), nil } - v, _ := strconv.ParseFloat(String(any), 32) - return float32(v) + v, err := strconv.ParseFloat(String(any), 32) + if err != nil { + return 0, gerror.WrapCodef( + gcode.CodeInvalidParameter, err, "converting string to float32 failed for: %v", any, + ) + } + return float32(v), nil } } } // Float64 converts `any` to float64. -func Float64(any interface{}) float64 { +func Float64(any any) float64 { + v, _ := doFloat64(any) + return v +} + +func doFloat64(any any) (float64, error) { if any == nil { - return 0 + return 0, nil } switch value := any.(type) { case float32: - return float64(value) + return float64(value), nil case float64: - return value + return value, nil case []byte: // TODO: It might panic here for these types. - return gbinary.DecodeToFloat64(value) + return gbinary.DecodeToFloat64(value), nil default: rv := reflect.ValueOf(any) switch rv.Kind() { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - return float64(rv.Int()) + return float64(rv.Int()), nil case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - return float64(rv.Uint()) + return float64(rv.Uint()), nil case reflect.Uintptr: - return float64(rv.Uint()) + return float64(rv.Uint()), nil case reflect.Float32, reflect.Float64: // Please Note: // When the type is float32 or a custom type defined based on float32, // switching to float64 may result in a few extra decimal places. - return rv.Float() + return rv.Float(), nil case reflect.Bool: if rv.Bool() { - return 1 + return 1, nil } - return 0 + return 0, nil case reflect.String: - f, _ := strconv.ParseFloat(rv.String(), 64) - return f + f, err := strconv.ParseFloat(rv.String(), 64) + if err != nil { + return 0, gerror.WrapCodef( + gcode.CodeInvalidParameter, err, "converting string to float64 failed for: %v", any, + ) + } + return f, nil case reflect.Ptr: if rv.IsNil() { - return 0 + return 0, nil } if f, ok := value.(localinterface.IFloat64); ok { - return f.Float64() + return f.Float64(), nil } - return Float64(rv.Elem().Interface()) + return doFloat64(rv.Elem().Interface()) default: if f, ok := value.(localinterface.IFloat64); ok { - return f.Float64() + return f.Float64(), nil } - v, _ := strconv.ParseFloat(String(any), 64) - return v + v, err := strconv.ParseFloat(String(any), 64) + if err != nil { + return 0, gerror.WrapCodef( + gcode.CodeInvalidParameter, err, "converting string to float64 failed for: %v", any, + ) + } + return v, nil } } } diff --git a/util/gconv/gconv_int.go b/util/gconv/gconv_int.go index 8b389f754..7e1e6a129 100644 --- a/util/gconv/gconv_int.go +++ b/util/gconv/gconv_int.go @@ -12,85 +12,119 @@ import ( "strconv" "github.com/gogf/gf/v2/encoding/gbinary" + "github.com/gogf/gf/v2/errors/gcode" + "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/util/gconv/internal/localinterface" ) // Int converts `any` to int. -func Int(any interface{}) int { - if any == nil { - return 0 - } +func Int(any any) int { + v, _ := doInt(any) + return v +} + +func doInt(any any) (int, error) { if v, ok := any.(int); ok { - return v + return v, nil } - return int(Int64(any)) + v, err := doInt64(any) + if err != nil { + return 0, err + } + return int(v), nil } // Int8 converts `any` to int8. -func Int8(any interface{}) int8 { - if any == nil { - return 0 - } +func Int8(any any) int8 { + v, _ := doInt8(any) + return v +} + +func doInt8(any any) (int8, error) { if v, ok := any.(int8); ok { - return v + return v, nil } - return int8(Int64(any)) + v, err := doInt64(any) + if err != nil { + return 0, err + } + return int8(v), nil } // Int16 converts `any` to int16. -func Int16(any interface{}) int16 { - if any == nil { - return 0 - } +func Int16(any any) int16 { + v, _ := doInt16(any) + return v +} + +func doInt16(any any) (int16, error) { if v, ok := any.(int16); ok { - return v + return v, nil } - return int16(Int64(any)) + v, err := doInt64(any) + if err != nil { + return 0, err + } + return int16(v), nil } // Int32 converts `any` to int32. -func Int32(any interface{}) int32 { - if any == nil { - return 0 - } +func Int32(any any) int32 { + v, _ := doInt32(any) + return v +} + +func doInt32(any any) (int32, error) { if v, ok := any.(int32); ok { - return v + return v, nil } - return int32(Int64(any)) + v, err := doInt64(any) + if err != nil { + return 0, err + } + return int32(v), nil } // Int64 converts `any` to int64. -func Int64(any interface{}) int64 { +func Int64(any any) int64 { + v, _ := doInt64(any) + return v +} + +func doInt64(any any) (int64, error) { if any == nil { - return 0 + return 0, nil + } + if v, ok := any.(int64); ok { + return v, nil } rv := reflect.ValueOf(any) switch rv.Kind() { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - return int64(rv.Int()) + return rv.Int(), nil case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - return int64(rv.Uint()) + return int64(rv.Uint()), nil case reflect.Uintptr: - return int64(rv.Uint()) + return int64(rv.Uint()), nil case reflect.Float32, reflect.Float64: - return int64(rv.Float()) + return int64(rv.Float()), nil case reflect.Bool: if rv.Bool() { - return 1 + return 1, nil } - return 0 + return 0, nil case reflect.Ptr: if rv.IsNil() { - return 0 + return 0, nil } if f, ok := any.(localinterface.IInt64); ok { - return f.Int64() + return f.Int64(), nil } - return Int64(rv.Elem().Interface()) + return doInt64(rv.Elem().Interface()) case reflect.Slice: // TODO: It might panic here for these types. if rv.Type().Elem().Kind() == reflect.Uint8 { - return gbinary.DecodeToInt64(rv.Bytes()) + return gbinary.DecodeToInt64(rv.Bytes()), nil } case reflect.String: var ( @@ -109,31 +143,39 @@ func Int64(any interface{}) int64 { if len(s) > 2 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X') { if v, e := strconv.ParseInt(s[2:], 16, 64); e == nil { if isMinus { - return -v + return -v, nil } - return v + return v, nil } } // Decimal. if v, e := strconv.ParseInt(s, 10, 64); e == nil { if isMinus { - return -v + return -v, nil } - return v + return v, nil } // Float64. - if valueInt64 := Float64(s); math.IsNaN(valueInt64) { - return 0 + valueInt64, err := doFloat64(s) + if err != nil { + return 0, err + } + if math.IsNaN(valueInt64) { + return 0, nil } else { if isMinus { - return -int64(valueInt64) + return -int64(valueInt64), nil } - return int64(valueInt64) + return int64(valueInt64), nil } default: if f, ok := any.(localinterface.IInt64); ok { - return f.Int64() + return f.Int64(), nil } } - return 0 + return 0, gerror.NewCodef( + gcode.CodeInvalidParameter, + `unsupport value type for converting to int64: %v`, + reflect.TypeOf(any), + ) } diff --git a/util/gconv/gconv_map.go b/util/gconv/gconv_map.go index 459c438c8..46809da6f 100644 --- a/util/gconv/gconv_map.go +++ b/util/gconv/gconv_map.go @@ -539,6 +539,8 @@ func doMapConvertForMapOrStructValue(in doMapConvertForMapOrStructValueInput) in }) } return array + + default: } return in.Value } diff --git a/util/gconv/gconv_scan.go b/util/gconv/gconv_scan.go index 8b58788ac..376a2b1aa 100644 --- a/util/gconv/gconv_scan.go +++ b/util/gconv/gconv_scan.go @@ -16,14 +16,21 @@ import ( ) // Scan automatically checks the type of `pointer` and converts `params` to `pointer`. -// It supports `pointer` in type of `*map/*[]map/*[]*map/*struct/**struct/*[]struct/*[]*struct` for converting. +// It supports various types of parameter conversions, including: +// 1. Basic types (int, string, float, etc.) +// 2. Pointer types +// 3. Slice types +// 4. Map types +// 5. Struct types // -// TODO change `paramKeyToAttrMap` to `ScanOption` to be more scalable; add `DeepCopy` option for `ScanOption`. -func Scan(srcValue interface{}, dstPointer interface{}, paramKeyToAttrMap ...map[string]string) (err error) { +// The `paramKeyToAttrMap` parameter is used for mapping between attribute names and parameter keys. +// TODO: change `paramKeyToAttrMap` to `ScanOption` to be more scalable; add `DeepCopy` option for `ScanOption`. +func Scan(srcValue any, dstPointer any, paramKeyToAttrMap ...map[string]string) (err error) { + // Check if srcValue is nil, in which case no conversion is needed if srcValue == nil { - // If `srcValue` is nil, no conversion. return nil } + // Check if dstPointer is nil, which is an invalid parameter if dstPointer == nil { return gerror.NewCode( gcode.CodeInvalidParameter, @@ -31,15 +38,7 @@ func Scan(srcValue interface{}, dstPointer interface{}, paramKeyToAttrMap ...map ) } - // json converting check. - ok, err := doConvertWithJsonCheck(srcValue, dstPointer) - if err != nil { - return err - } - if ok { - return nil - } - + // Get the reflection type and value of dstPointer var ( dstPointerReflectType reflect.Type dstPointerReflectValue reflect.Value @@ -49,18 +48,20 @@ func Scan(srcValue interface{}, dstPointer interface{}, paramKeyToAttrMap ...map dstPointerReflectType = v.Type() } else { dstPointerReflectValue = reflect.ValueOf(dstPointer) - // do not use dstPointerReflectValue.Type() as dstPointerReflectValue might be zero. + // Do not use dstPointerReflectValue.Type() as dstPointerReflectValue might be zero dstPointerReflectType = reflect.TypeOf(dstPointer) } - // pointer kind validation. + // Validate the kind of dstPointer var dstPointerReflectKind = dstPointerReflectType.Kind() if dstPointerReflectKind != reflect.Ptr { + // If dstPointer is not a pointer, try to get its address if dstPointerReflectValue.CanAddr() { dstPointerReflectValue = dstPointerReflectValue.Addr() dstPointerReflectType = dstPointerReflectValue.Type() dstPointerReflectKind = dstPointerReflectType.Kind() } else { + // If dstPointer is not a pointer and cannot be addressed, return an error return gerror.NewCodef( gcode.CodeInvalidParameter, `destination pointer should be type of pointer, but got type: %v`, @@ -68,23 +69,144 @@ func Scan(srcValue interface{}, dstPointer interface{}, paramKeyToAttrMap ...map ) } } - // direct assignment checks! + + // Get the reflection value of srcValue var srcValueReflectValue reflect.Value if v, ok := srcValue.(reflect.Value); ok { srcValueReflectValue = v } else { srcValueReflectValue = reflect.ValueOf(srcValue) } - // if `srcValue` and `dstPointer` are the same type, the do directly assignment. - // For performance enhancement purpose. - var dstPointerReflectValueElem = dstPointerReflectValue.Elem() - // if `srcValue` and `dstPointer` are the same type, the do directly assignment. - // for performance enhancement purpose. - if ok = doConvertWithTypeCheck(srcValueReflectValue, dstPointerReflectValueElem); ok { + + // Get the element type and kind of dstPointer + var ( + dstPointerReflectValueElem = dstPointerReflectValue.Elem() + dstPointerReflectValueElemKind = dstPointerReflectValueElem.Kind() + ) + // Handle multiple level pointers + if dstPointerReflectValueElemKind == reflect.Ptr { + if dstPointerReflectValueElem.IsNil() { + // Create a new value for the pointer dereference + nextLevelPtr := reflect.New(dstPointerReflectValueElem.Type().Elem()) + // Recursively scan into the dereferenced pointer + if err = Scan(srcValueReflectValue, nextLevelPtr, paramKeyToAttrMap...); err == nil { + dstPointerReflectValueElem.Set(nextLevelPtr) + } + return + } + return Scan(srcValueReflectValue, dstPointerReflectValueElem, paramKeyToAttrMap...) + } + + // Check if srcValue and dstPointer are the same type, in which case direct assignment can be performed + if ok := doConvertWithTypeCheck(srcValueReflectValue, dstPointerReflectValueElem); ok { return nil } - // do the converting. + // Handle different destination types + switch dstPointerReflectValueElemKind { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + // Convert to int type + dstPointerReflectValueElem.SetInt(Int64(srcValue)) + return nil + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + // Convert to uint type + dstPointerReflectValueElem.SetUint(Uint64(srcValue)) + return nil + + case reflect.Float32, reflect.Float64: + // Convert to float type + dstPointerReflectValueElem.SetFloat(Float64(srcValue)) + return nil + + case reflect.String: + // Convert to string type + dstPointerReflectValueElem.SetString(String(srcValue)) + return nil + + case reflect.Bool: + // Convert to bool type + dstPointerReflectValueElem.SetBool(Bool(srcValue)) + return nil + + case reflect.Slice: + // Handle slice type conversion + var ( + dstElemType = dstPointerReflectValueElem.Type().Elem() + dstElemKind = dstElemType.Kind() + ) + // The slice element might be a pointer type + if dstElemKind == reflect.Ptr { + dstElemType = dstElemType.Elem() + dstElemKind = dstElemType.Kind() + } + // Special handling for struct or map slice elements + if dstElemKind == reflect.Struct || dstElemKind == reflect.Map { + return doScanForComplicatedTypes(srcValue, dstPointer, dstPointerReflectType, paramKeyToAttrMap...) + } + // Handle basic type slice conversions + var srcValueReflectValueKind = srcValueReflectValue.Kind() + if srcValueReflectValueKind == reflect.Slice || srcValueReflectValueKind == reflect.Array { + var ( + srcLen = srcValueReflectValue.Len() + newSlice = reflect.MakeSlice(dstPointerReflectValueElem.Type(), srcLen, srcLen) + ) + for i := 0; i < srcLen; i++ { + srcElem := srcValueReflectValue.Index(i).Interface() + switch dstElemType.Kind() { + case reflect.String: + newSlice.Index(i).SetString(String(srcElem)) + case reflect.Int: + newSlice.Index(i).SetInt(Int64(srcElem)) + case reflect.Int64: + newSlice.Index(i).SetInt(Int64(srcElem)) + case reflect.Float64: + newSlice.Index(i).SetFloat(Float64(srcElem)) + case reflect.Bool: + newSlice.Index(i).SetBool(Bool(srcElem)) + default: + return Scan( + srcElem, newSlice.Index(i).Addr().Interface(), paramKeyToAttrMap..., + ) + } + } + dstPointerReflectValueElem.Set(newSlice) + return nil + } + return doScanForComplicatedTypes(srcValue, dstPointer, dstPointerReflectType, paramKeyToAttrMap...) + + default: + // Handle complex types (structs, maps, etc.) + return doScanForComplicatedTypes(srcValue, dstPointer, dstPointerReflectType, paramKeyToAttrMap...) + } +} + +// doScanForComplicatedTypes handles the scanning of complex data types. +// It supports converting between maps, structs, and slices of these types. +// The function first attempts JSON conversion, then falls back to specific type handling. +// +// It supports `pointer` in type of `*map/*[]map/*[]*map/*struct/**struct/*[]struct/*[]*struct` for converting. +// +// Parameters: +// - srcValue: The source value to convert from +// - dstPointer: The destination pointer to convert to +// - dstPointerReflectType: The reflection type of the destination pointer +// - paramKeyToAttrMap: Optional mapping between parameter keys and struct attribute names +func doScanForComplicatedTypes( + srcValue, dstPointer any, + dstPointerReflectType reflect.Type, + paramKeyToAttrMap ...map[string]string, +) error { + // Try JSON conversion first + ok, err := doConvertWithJsonCheck(srcValue, dstPointer) + if err != nil { + return err + } + if ok { + return nil + } + + // Handle specific type conversions var ( dstPointerReflectTypeElem = dstPointerReflectType.Elem() dstPointerReflectTypeElemKind = dstPointerReflectTypeElem.Kind() @@ -93,8 +215,11 @@ func Scan(srcValue interface{}, dstPointer interface{}, paramKeyToAttrMap ...map if len(paramKeyToAttrMap) > 0 { keyToAttributeNameMapping = paramKeyToAttrMap[0] } + + // Handle different destination types switch dstPointerReflectTypeElemKind { case reflect.Map: + // Convert map to map return doMapToMap(srcValue, dstPointer, paramKeyToAttrMap...) case reflect.Array, reflect.Slice: @@ -102,53 +227,59 @@ func Scan(srcValue interface{}, dstPointer interface{}, paramKeyToAttrMap ...map sliceElem = dstPointerReflectTypeElem.Elem() sliceElemKind = sliceElem.Kind() ) + // Handle pointer elements for sliceElemKind == reflect.Ptr { sliceElem = sliceElem.Elem() sliceElemKind = sliceElem.Kind() } if sliceElemKind == reflect.Map { + // Convert to slice of maps return doMapToMaps(srcValue, dstPointer, paramKeyToAttrMap...) } + // Convert to slice of structs return doStructs(srcValue, dstPointer, keyToAttributeNameMapping, "") default: + // Convert to single struct return doStruct(srcValue, dstPointer, keyToAttributeNameMapping, "") } } +// doConvertWithTypeCheck supports `pointer` in type of `*map/*[]map/*[]*map/*struct/**struct/*[]struct/*[]*struct` +// for converting. func doConvertWithTypeCheck(srcValueReflectValue, dstPointerReflectValueElem reflect.Value) (ok bool) { if !dstPointerReflectValueElem.IsValid() || !srcValueReflectValue.IsValid() { return false } switch { - // Example: - // UploadFile => UploadFile - // []UploadFile => []UploadFile - // *UploadFile => *UploadFile - // *[]UploadFile => *[]UploadFile - // map => map - // []map => []map - // *[]map => *[]map + // Examples: + // UploadFile => UploadFile + // []UploadFile => []UploadFile + // *UploadFile => *UploadFile + // *[]UploadFile => *[]UploadFile + // map[int][int] => map[int][int] + // []map[int][int] => []map[int][int] + // *[]map[int][int] => *[]map[int][int] case dstPointerReflectValueElem.Type() == srcValueReflectValue.Type(): dstPointerReflectValueElem.Set(srcValueReflectValue) return true - // Example: - // UploadFile => *UploadFile - // []UploadFile => *[]UploadFile - // map => *map - // []map => *[]map + // Examples: + // UploadFile => *UploadFile + // []UploadFile => *[]UploadFile + // map[int][int] => *map[int][int] + // []map[int][int] => *[]map[int][int] case dstPointerReflectValueElem.Kind() == reflect.Ptr && dstPointerReflectValueElem.Elem().IsValid() && dstPointerReflectValueElem.Elem().Type() == srcValueReflectValue.Type(): dstPointerReflectValueElem.Elem().Set(srcValueReflectValue) return true - // Example: - // *UploadFile => UploadFile - // *[]UploadFile => []UploadFile - // *map => map - // *[]map => []map + // Examples: + // *UploadFile => UploadFile + // *[]UploadFile => []UploadFile + // *map[int][int] => map[int][int] + // *[]map[int][int] => []map[int][int] case srcValueReflectValue.Kind() == reflect.Ptr && srcValueReflectValue.Elem().IsValid() && dstPointerReflectValueElem.Type() == srcValueReflectValue.Elem().Type(): @@ -160,9 +291,18 @@ func doConvertWithTypeCheck(srcValueReflectValue, dstPointerReflectValueElem ref } } -// doConvertWithJsonCheck does json converting check. -// If given `params` is JSON, it then uses json.Unmarshal doing the converting. -func doConvertWithJsonCheck(srcValue interface{}, dstPointer interface{}) (ok bool, err error) { +// doConvertWithJsonCheck attempts to convert the source value to the destination +// using JSON marshaling and unmarshaling. This is particularly useful for complex +// types that can be represented as JSON. +// +// Parameters: +// - srcValue: The source value to convert from +// - dstPointer: The destination pointer to convert to +// +// Returns: +// - bool: true if JSON conversion was successful +// - error: any error that occurred during conversion +func doConvertWithJsonCheck(srcValue any, dstPointer any) (ok bool, err error) { switch valueResult := srcValue.(type) { case []byte: if json.Valid(valueResult) { diff --git a/util/gconv/gconv_uint.go b/util/gconv/gconv_uint.go index ac1938763..367a71a79 100644 --- a/util/gconv/gconv_uint.go +++ b/util/gconv/gconv_uint.go @@ -12,110 +12,174 @@ import ( "strconv" "github.com/gogf/gf/v2/encoding/gbinary" + "github.com/gogf/gf/v2/errors/gcode" + "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/util/gconv/internal/localinterface" ) // Uint converts `any` to uint. -func Uint(any interface{}) uint { +func Uint(any any) uint { + v, _ := doUint(any) + return v +} + +func doUint(any any) (uint, error) { if any == nil { - return 0 + return 0, nil } if v, ok := any.(uint); ok { - return v + return v, nil } - return uint(Uint64(any)) + v, err := doUint64(any) + return uint(v), err } // Uint8 converts `any` to uint8. -func Uint8(any interface{}) uint8 { +func Uint8(any any) uint8 { + v, _ := doUint8(any) + return v +} + +func doUint8(any any) (uint8, error) { if any == nil { - return 0 + return 0, nil } if v, ok := any.(uint8); ok { - return v + return v, nil } - return uint8(Uint64(any)) + v, err := doUint64(any) + return uint8(v), err } // Uint16 converts `any` to uint16. -func Uint16(any interface{}) uint16 { +func Uint16(any any) uint16 { + v, _ := doUint16(any) + return v +} + +func doUint16(any any) (uint16, error) { if any == nil { - return 0 + return 0, nil } if v, ok := any.(uint16); ok { - return v + return v, nil } - return uint16(Uint64(any)) + v, err := doUint64(any) + return uint16(v), err } // Uint32 converts `any` to uint32. -func Uint32(any interface{}) uint32 { +func Uint32(any any) uint32 { + v, _ := doUint32(any) + return v +} + +func doUint32(any any) (uint32, error) { if any == nil { - return 0 + return 0, nil } if v, ok := any.(uint32); ok { - return v + return v, nil } - return uint32(Uint64(any)) + v, err := doUint64(any) + return uint32(v), err } // Uint64 converts `any` to uint64. -func Uint64(any interface{}) uint64 { +func Uint64(any any) uint64 { + v, _ := doUint64(any) + return v +} + +func doUint64(any any) (uint64, error) { if any == nil { - return 0 + return 0, nil + } + if v, ok := any.(uint64); ok { + return v, nil } rv := reflect.ValueOf(any) switch rv.Kind() { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - return uint64(rv.Int()) + val := rv.Int() + if val < 0 { + return uint64(val), gerror.NewCodef( + gcode.CodeInvalidParameter, + `cannot convert negative value "%d" to uint64`, + val, + ) + } + return uint64(val), nil case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - return uint64(rv.Uint()) + return rv.Uint(), nil case reflect.Uintptr: - return uint64(rv.Uint()) + return rv.Uint(), nil case reflect.Float32, reflect.Float64: - return uint64(rv.Float()) + val := rv.Float() + if val < 0 { + return uint64(val), gerror.NewCodef( + gcode.CodeInvalidParameter, + `cannot convert negative value "%f" to uint64`, + val, + ) + } + return uint64(val), nil case reflect.Bool: if rv.Bool() { - return 1 + return 1, nil } - return 0 + return 0, nil case reflect.Ptr: if rv.IsNil() { - return 0 + return 0, nil } if f, ok := any.(localinterface.IUint64); ok { - return f.Uint64() + return f.Uint64(), nil } - return Uint64(rv.Elem().Interface()) + return doUint64(rv.Elem().Interface()) case reflect.Slice: - // TODO:These types should be panic if rv.Type().Elem().Kind() == reflect.Uint8 { - return gbinary.DecodeToUint64(rv.Bytes()) + return gbinary.DecodeToUint64(rv.Bytes()), nil } - case reflect.String: - var ( - s = rv.String() + return 0, gerror.NewCodef( + gcode.CodeInvalidParameter, + `unsupport slice type "%s" for converting to uint64`, + rv.Type().String(), ) + case reflect.String: + var s = rv.String() // Hexadecimal if len(s) > 2 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X') { - if v, e := strconv.ParseUint(s[2:], 16, 64); e == nil { - return v + v, err := strconv.ParseUint(s[2:], 16, 64) + if err == nil { + return v, nil } + return 0, gerror.WrapCodef( + gcode.CodeInvalidParameter, + err, + `cannot convert hexadecimal string "%s" to uint64`, + s, + ) } // Decimal - if v, e := strconv.ParseUint(s, 10, 64); e == nil { - return v + if v, err := strconv.ParseUint(s, 10, 64); err == nil { + return v, nil } // Float64 - if valueFloat64 := Float64(any); math.IsNaN(valueFloat64) { - return 0 - } else { - return uint64(valueFloat64) + if v, err := doFloat64(any); err == nil { + if math.IsNaN(v) { + return 0, nil + } + return uint64(v), nil } default: if f, ok := any.(localinterface.IUint64); ok { - return f.Uint64() + return f.Uint64(), nil } } - return 0 + return 0, gerror.NewCodef( + gcode.CodeInvalidParameter, + `unsupport value type "%s" for converting to uint64`, + reflect.TypeOf(any).String(), + ) } diff --git a/util/gconv/gconv_z_unit_scan_basic_types_test.go b/util/gconv/gconv_z_unit_scan_basic_types_test.go new file mode 100644 index 000000000..7e13faaab --- /dev/null +++ b/util/gconv/gconv_z_unit_scan_basic_types_test.go @@ -0,0 +1,171 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gconv_test + +import ( + "testing" + "time" + + "github.com/gogf/gf/v2/test/gtest" + "github.com/gogf/gf/v2/util/gconv" +) + +type testScan struct { + Src any + Dst any + Expect any +} + +func TestScanBasicTypes(t *testing.T) { + // Define test data structure + type User struct { + Name string + Age int + } + type UserWithTag struct { + Name string `json:"name"` + Age int `json:"age"` + } + + // Prepare test data + var testScanData = []testScan{ + // Basic type conversion + {1, new(int), 1}, + {int8(1), new(int16), int16(1)}, + {int16(1), new(int32), int32(1)}, + {int32(1), new(int64), int64(1)}, + {uint(1), new(int), 1}, + {uint8(1), new(int), 1}, + {uint16(1), new(int), 1}, + {uint32(1), new(int), 1}, + {uint64(1), new(int), 1}, + {float32(1.0), new(int), 1}, + {float64(1.0), new(int), 1}, + {true, new(int), 1}, + {false, new(int), 0}, + {"1", new(int), 1}, + {"true", new(bool), true}, + {"false", new(bool), false}, + {1, new(bool), true}, + {0, new(bool), false}, + + // String conversion + {1, new(string), "1"}, + {1.1, new(string), "1.1"}, + {true, new(string), "true"}, + {false, new(string), "false"}, + {[]byte("hello"), new(string), "hello"}, + + // Slice conversion + {[]int{1, 2, 3}, new([]string), []string{"1", "2", "3"}}, + {[]string{"1", "2", "3"}, new([]int), []int{1, 2, 3}}, + {`["1","2","3"]`, new([]string), []string{"1", "2", "3"}}, + {`[1,2,3]`, new([]int), []int{1, 2, 3}}, + + // Map conversion + { + map[string]interface{}{"name": "john", "age": 18}, + new(User), + &User{Name: "john", Age: 18}, + }, + { + `{"name":"john","age":18}`, + new(User), + &User{Name: "john", Age: 18}, + }, + { + map[string]interface{}{"name": "john", "age": 18}, + new(UserWithTag), + &UserWithTag{Name: "john", Age: 18}, + }, + { + map[string]string{"name": "john", "age": "18"}, + new(map[string]interface{}), + &map[string]interface{}{"name": "john", "age": "18"}, + }, + + // Struct conversion + { + User{Name: "john", Age: 18}, + new(map[string]interface{}), + &map[string]interface{}{"Name": "john", "Age": 18}, + }, + { + &User{Name: "john", Age: 18}, + new(UserWithTag), + &UserWithTag{Name: "john", Age: 18}, + }, + + // Special cases + {nil, new(interface{}), nil}, + {nil, new(*int), (*int)(nil)}, + {[]byte(nil), new(string), ""}, + {"", new(int), 0}, + {"", new(float64), 0.0}, + {"", new(bool), false}, + + // Time type + {time.Date(2023, 1, 2, 0, 0, 0, 0, time.Local), new(string), "2023-01-02 00:00:00"}, + + // Pointer conversion + {&User{Name: "john"}, new(*User), &User{Name: "john"}}, + } + + // Basic types test. + gtest.C(t, func(t *gtest.T) { + for _, v := range testScanData { + // t.Logf(`%#v`, v) + err := gconv.Scan(v.Src, v.Dst) + t.AssertNil(err) + } + }) + + // int -> **int + gtest.C(t, func(t *gtest.T) { + var ( + v = 100 + i *int + ) + err := gconv.Scan(v, &i) + t.AssertNil(err) + t.AssertNE(i, nil) + t.Assert(*i, v) + }) + // *int -> **int + gtest.C(t, func(t *gtest.T) { + var ( + v = 100 + i *int + ) + err := gconv.Scan(&v, &i) + t.AssertNil(err) + t.AssertNE(i, nil) + t.Assert(*i, v) + }) + // string -> **string + gtest.C(t, func(t *gtest.T) { + var ( + v = "1000" + i *string + ) + err := gconv.Scan(v, &i) + t.AssertNil(err) + t.AssertNE(i, nil) + t.Assert(*i, v) + }) + // *string -> **string + gtest.C(t, func(t *gtest.T) { + var ( + v = "1000" + i *string + ) + err := gconv.Scan(&v, &i) + t.AssertNil(err) + t.AssertNE(i, nil) + t.Assert(*i, v) + }) +} diff --git a/util/gconv/gconv_z_unit_scan_test.go b/util/gconv/gconv_z_unit_scan_test.go index 4790565dd..76620d1f4 100644 --- a/util/gconv/gconv_z_unit_scan_test.go +++ b/util/gconv/gconv_z_unit_scan_test.go @@ -362,29 +362,15 @@ func TestScan(t *testing.T) { mapParameter = map[string]string{"Name": "Place", "Place": "Name"} ) - // TODO: The following test cases should be working, but they are not. - //err = gconv.Scan(test, &scanExpects.mapStrStr, mapParameter) - //t.AssertNil(err) - //t.Assert(test["Name"], scanExpects.mapStrStr["Place"]) - //t.Assert(test["Place"], scanExpects.mapStrStr["Name"]) - // - //err = gconv.Scan(test, &scanExpects.mapStrAny, mapParameter) - //t.AssertNil(err) - //t.Assert(test["Name"], scanExpects.mapStrAny["Place"]) - //t.Assert(test["Place"], scanExpects.mapStrAny["Name"]) - // - //err = gconv.Scan(test, &scanExpects.mapAnyAny, mapParameter) - //t.AssertNil(err) - //t.Assert(test["Name"], scanExpects.mapAnyAny["Place"]) - //t.Assert(test["Place"], scanExpects.mapAnyAny["Name"]) - err = gconv.Scan(test, &scanExpects.structSub, mapParameter) t.AssertNil(err) t.Assert(test["Name"], scanExpects.structSub.Place) t.Assert(test["Place"], scanExpects.structSub.Name) + //t.Logf("%#v", test) err = gconv.Scan(test, &scanExpects.structSubPtr, mapParameter) t.AssertNil(err) + //t.Logf("%#v", scanExpects.structSubPtr) t.Assert(test["Name"], scanExpects.structSubPtr.Place) t.Assert(test["Place"], scanExpects.structSubPtr.Name) } diff --git a/util/gconv/gconv_z_unit_uint_test.go b/util/gconv/gconv_z_unit_uint_test.go index 2908a79b9..58ae283d0 100644 --- a/util/gconv/gconv_z_unit_uint_test.go +++ b/util/gconv/gconv_z_unit_uint_test.go @@ -120,8 +120,9 @@ var uintTests = []struct { func TestUint(t *testing.T) { gtest.C(t, func(t *gtest.T) { - for _, test := range uintTests { - t.AssertEQ(gconv.Uint(test.value), test.expect) + for _, v := range uintTests { + //t.Logf(`%+v`, v) + t.AssertEQ(gconv.Uint(v.value), v.expect) } }) }