Files
gf/util/gconv/gconv_z_unit_scan_test.go
hailaz ee24da4e72 refactor: interface{} to any and reflect.Ptr to reflect.Pointer (#4395)
This pull request standardizes the use of the Go 1.18+ `any` type alias
instead of `interface{}` throughout the codebase. The change improves
code readability and aligns with modern Go best practices. The update
touches many files, including core data structures, code generation
templates, logging utilities, and test data, ensuring consistency across
all usages.

**Type alias migration to `any`:**

* Replaced all instances of `interface{}` with `any` in core data
structures such as `garray` and in generated model structs (e.g.,
`TableUser`, `User1`, `User2`) to modernize type usage.
[[1]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L31-R31)
[[2]](diffhunk://#diff-6c19859cb32c7516ea95ddc8f8235460818eb2f24d2204308e0d9e1b19e7d90fL15-R19)
[[3]](diffhunk://#diff-a15ba2f5e830b4833c47b902515a4f9e5a4f83a3707698f3229b307ec3776b41L15-R18)
[[4]](diffhunk://#diff-52e0837e84d49221d1b810d88fdf78221f36cffcd664fb42f8aba49a79b974dcL15-R19)
[[5]](diffhunk://#diff-11c3457d1a23a4ca6ecd00d6b856289774936b6a708384cf03aff164044e7546L15-R19)
[[6]](diffhunk://#diff-2cff9cf8e6a0cc34087326d8c8149c3bbaf74c76fdbdf5a73daed13cc04249e1L15-R19)
* Updated function signatures, method parameters, and return types from
`interface{}` to `any` in various parts of the codebase, including code
generation, service logic, and logging utilities (e.g., `mlog`).
[[1]](diffhunk://#diff-175edfeea54490b8fe4e18ffcbea5835efaf8f0b8acf623359073987cae7eb76L48-R55)
[[2]](diffhunk://#diff-2b1953fb78cf3593d8c2c7d911e95b65fd0b847c30ed0b4d167d16fe6d781235L54-R74)
[[3]](diffhunk://#diff-e001b7a4b63603b9b14f00de78a4d570bb76c5f57d856a24643f071032e12356L66-R73)
[[4]](diffhunk://#diff-5582954e8a9983988dc8854ad82067fb2ac6269b988e07357ad8db1dfec5f1a0L39-R41)
[[5]](diffhunk://#diff-c5d51d56f487779a2b6207c7ad26c7a20bbadcc846ce094fe60ab4cabff58c51L107-R107)
[[6]](diffhunk://#diff-f96e6a9fdb416eb1804ceaba1fe0ac637bff22c43837f8bb849c2366ce72d4a1L116-R121)
[[7]](diffhunk://#diff-f94c83a1b08ae060d9346f4a6031fc4a7b9a0b894e02d9afaa09018b6598eac0L112-R112)
[[8]](diffhunk://#diff-748b11dbe8828dd4c040ec23cae0b8fe57ecf0a2d1b7694ea39102294e633c64L36-R36)
[[9]](diffhunk://#diff-748b11dbe8828dd4c040ec23cae0b8fe57ecf0a2d1b7694ea39102294e633c64L74-R74)
[[10]](diffhunk://#diff-748b11dbe8828dd4c040ec23cae0b8fe57ecf0a2d1b7694ea39102294e633c64L96-R96)

**Generated code and templates:**

* Adjusted generated files and code generation templates to output `any`
instead of `interface{}` for relevant struct fields and function
signatures, ensuring that new code generation aligns with the updated
convention.
[[1]](diffhunk://#diff-6c19859cb32c7516ea95ddc8f8235460818eb2f24d2204308e0d9e1b19e7d90fL15-R19)
[[2]](diffhunk://#diff-a15ba2f5e830b4833c47b902515a4f9e5a4f83a3707698f3229b307ec3776b41L15-R18)
[[3]](diffhunk://#diff-52e0837e84d49221d1b810d88fdf78221f36cffcd664fb42f8aba49a79b974dcL15-R19)
[[4]](diffhunk://#diff-11c3457d1a23a4ca6ecd00d6b856289774936b6a708384cf03aff164044e7546L15-R19)
[[5]](diffhunk://#diff-2cff9cf8e6a0cc34087326d8c8149c3bbaf74c76fdbdf5a73daed13cc04249e1L15-R19)
[[6]](diffhunk://#diff-175edfeea54490b8fe4e18ffcbea5835efaf8f0b8acf623359073987cae7eb76L48-R55)
[[7]](diffhunk://#diff-e001b7a4b63603b9b14f00de78a4d570bb76c5f57d856a24643f071032e12356L66-R73)
[[8]](diffhunk://#diff-5582954e8a9983988dc8854ad82067fb2ac6269b988e07357ad8db1dfec5f1a0L39-R41)

**Container and utility updates:**

* Refactored the `garray` container implementation and related
constructors/methods to use `[]any` instead of `[]interface{}`, along
with corresponding function signatures.
[[1]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L31-R31)
[[2]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L52-R52)
[[3]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L62-R62)
[[4]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L73-R86)
[[5]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L96-R97)
[[6]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L107-R114)
[[7]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L124-R124)
[[8]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L135-R143)
[[9]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L167-R167)

These changes collectively modernize the codebase and prepare it for
future Go developments by using the idiomatic `any` type.
2025-08-28 16:53:19 +08:00

444 lines
13 KiB
Go

// 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 (
"fmt"
"testing"
"github.com/gogf/gf/v2/container/gvar"
"github.com/gogf/gf/v2/encoding/gjson"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gtime"
"github.com/gogf/gf/v2/test/gtest"
"github.com/gogf/gf/v2/util/gconv"
)
type scanStructTest struct {
Name string
Place string
}
type scanExpectTest struct {
mapStrStr map[string]string
mapStrAny map[string]any
mapAnyAny map[any]any
structSub scanStructTest
structSubPtr *scanStructTest
}
var scanValueMapsTest = []map[string]any{
{"Name": false, "Place": true},
{"Name": int(0), "Place": int(1)},
{"Name": int8(0), "Place": int8(1)},
{"Name": int16(0), "Place": int16(1)},
{"Name": int32(0), "Place": int32(1)},
{"Name": int64(0), "Place": int64(1)},
{"Name": uint(0), "Place": uint(1)},
{"Name": uint8(0), "Place": uint8(1)},
{"Name": uint16(0), "Place": uint16(1)},
{"Name": uint32(0), "Place": uint32(1)},
{"Name": uint64(0), "Place": uint64(1)},
{"Name": float32(0), "Place": float32(1)},
{"Name": float64(0), "Place": float64(1)},
{"Name": "Mercury", "Place": "卡罗利斯盆地"},
{"Name": []byte("Saturn"), "Place": []byte("土星环")},
{"Name": complex64(0), "Place": complex64(1 + 2i)},
{"Name": complex128(0), "Place": complex128(1 + 2i)},
{"Name": any(0), "Place": any("1")},
{"Name": gvar.New("Jupiter"), "Place": gvar.New("大红斑")},
{"Name": gtime.New("2024-01-01 01:01:01"), "Place": gtime.New("2021-01-01 01:01:01")},
{"Name": map[string]string{"Name": "Sun"}, "Place": map[string]string{"Place": "太阳黑子"}},
{"Name": []string{"Earth", "Moon"}, "Place": []string{"好望角", "万户环形山"}},
}
var scanValueStructsTest = []scanStructTest{
{"Venus", "阿佛洛狄特高原"},
}
var scanValueJsonTest = []string{
`{"Name": "Mars", "Place": "奥林帕斯山"}`,
}
var scanExpects = scanExpectTest{
mapStrStr: make(map[string]string),
mapStrAny: make(map[string]any),
mapAnyAny: make(map[any]any),
structSub: scanStructTest{},
structSubPtr: &scanStructTest{},
}
func TestScan(t *testing.T) {
// Test for map converting.
gtest.C(t, func(t *gtest.T) {
scanValuesTest := scanValueMapsTest
for _, test := range scanValuesTest {
var (
err error
scanExpects = scanExpects
)
err = gconv.Scan(test, &scanExpects.mapStrStr)
t.AssertNil(err)
t.Assert(test["Name"], scanExpects.mapStrStr["Name"])
t.Assert(test["Place"], scanExpects.mapStrStr["Place"])
err = gconv.Scan(test, &scanExpects.mapStrAny)
t.AssertNil(err)
t.Assert(test["Name"], scanExpects.mapStrAny["Name"])
t.Assert(test["Place"], scanExpects.mapStrAny["Place"])
err = gconv.Scan(test, &scanExpects.mapAnyAny)
t.AssertNil(err)
t.Assert(test["Name"], scanExpects.mapAnyAny["Name"])
t.Assert(test["Place"], scanExpects.mapAnyAny["Place"])
err = gconv.Scan(test, &scanExpects.structSub)
t.AssertNil(err)
t.Assert(test["Name"], scanExpects.structSub.Name)
t.Assert(test["Place"], scanExpects.structSub.Place)
err = gconv.Scan(test, &scanExpects.structSubPtr)
t.AssertNil(err)
t.Assert(test["Name"], scanExpects.structSubPtr.Name)
t.Assert(test["Place"], scanExpects.structSubPtr.Place)
}
})
// Test for slice map converting.
gtest.C(t, func(t *gtest.T) {
scanValuesTest := scanValueMapsTest
for _, test := range scanValuesTest {
var (
err error
scanExpects = scanExpects
maps = []map[string]any{test, test}
)
var mss = []map[string]string{scanExpects.mapStrStr, scanExpects.mapStrStr}
err = gconv.Scan(maps, &mss)
t.AssertNil(err)
t.Assert(len(mss), len(maps))
for k := range maps {
t.Assert(maps[k]["Name"], mss[k]["Name"])
t.Assert(maps[k]["Place"], mss[k]["Place"])
}
var msa = []map[string]any{scanExpects.mapStrAny, scanExpects.mapStrAny}
err = gconv.Scan(maps, &msa)
t.AssertNil(err)
t.Assert(len(msa), len(maps))
for k := range maps {
t.Assert(maps[k]["Name"], msa[k]["Name"])
t.Assert(maps[k]["Place"], msa[k]["Place"])
}
var maa = []map[any]any{scanExpects.mapAnyAny, scanExpects.mapAnyAny}
err = gconv.Scan(maps, &maa)
t.AssertNil(err)
t.Assert(len(maa), len(maps))
for k := range maps {
t.Assert(maps[k]["Name"], maa[k]["Name"])
t.Assert(maps[k]["Place"], maa[k]["Place"])
}
var ss = []scanStructTest{scanExpects.structSub, scanExpects.structSub}
err = gconv.Scan(maps, &ss)
t.AssertNil(err)
t.Assert(len(ss), len(maps))
for k := range maps {
t.Assert(maps[k]["Name"], ss[k].Name)
t.Assert(maps[k]["Place"], ss[k].Place)
}
var ssp = []*scanStructTest{scanExpects.structSubPtr, scanExpects.structSubPtr}
err = gconv.Scan(maps, &ssp)
t.AssertNil(err)
t.Assert(len(ssp), len(maps))
for k := range maps {
t.Assert(maps[k]["Name"], ssp[k].Name)
t.Assert(maps[k]["Place"], ssp[k].Place)
}
}
})
// Test for struct converting.
gtest.C(t, func(t *gtest.T) {
scanValuesTest := scanValueStructsTest
for _, test := range scanValuesTest {
var (
err error
scanExpects = scanExpects
)
err = gconv.Scan(test, &scanExpects.mapStrStr)
t.AssertNil(err)
t.Assert(test.Name, scanExpects.mapStrStr["Name"])
t.Assert(test.Place, scanExpects.mapStrStr["Place"])
err = gconv.Scan(test, &scanExpects.mapStrAny)
t.AssertNil(err)
t.Assert(test.Name, scanExpects.mapStrAny["Name"])
t.Assert(test.Place, scanExpects.mapStrAny["Place"])
err = gconv.Scan(test, &scanExpects.mapAnyAny)
t.AssertNil(err)
t.Assert(test.Name, scanExpects.mapAnyAny["Name"])
t.Assert(test.Place, scanExpects.mapAnyAny["Place"])
err = gconv.Scan(test, &scanExpects.structSub)
t.AssertNil(err)
t.Assert(test.Name, scanExpects.structSub.Name)
t.Assert(test.Place, scanExpects.structSub.Place)
err = gconv.Scan(test, &scanExpects.structSubPtr)
t.AssertNil(err)
t.Assert(test.Name, scanExpects.structSubPtr.Name)
t.Assert(test.Place, scanExpects.structSubPtr.Place)
}
})
// Test for slice struct converting.
gtest.C(t, func(t *gtest.T) {
scanValuesTest := scanValueStructsTest
for _, test := range scanValuesTest {
var (
err error
scanExpects = scanExpects
structs = []scanStructTest{test, test}
)
var mss = []map[string]string{scanExpects.mapStrStr, scanExpects.mapStrStr}
err = gconv.Scan(structs, &mss)
t.AssertNil(err)
t.Assert(len(mss), len(structs))
for k := range structs {
t.Assert(structs[k].Name, mss[k]["Name"])
t.Assert(structs[k].Place, mss[k]["Place"])
}
var msa = []map[string]any{scanExpects.mapStrAny, scanExpects.mapStrAny}
err = gconv.Scan(structs, &msa)
t.AssertNil(err)
t.Assert(len(msa), len(structs))
for k := range structs {
t.Assert(structs[k].Name, msa[k]["Name"])
t.Assert(structs[k].Place, msa[k]["Place"])
}
var maa = []map[any]any{scanExpects.mapAnyAny, scanExpects.mapAnyAny}
err = gconv.Scan(structs, &maa)
t.AssertNil(err)
t.Assert(len(maa), len(structs))
for k := range structs {
t.Assert(structs[k].Name, maa[k]["Name"])
t.Assert(structs[k].Place, maa[k]["Place"])
}
var ss = []scanStructTest{scanExpects.structSub, scanExpects.structSub}
err = gconv.Scan(structs, &ss)
t.AssertNil(err)
t.Assert(len(ss), len(structs))
for k := range structs {
t.Assert(structs[k].Name, ss[k].Name)
t.Assert(structs[k].Place, ss[k].Place)
}
var ssp = []*scanStructTest{scanExpects.structSubPtr, scanExpects.structSubPtr}
err = gconv.Scan(structs, &ssp)
t.AssertNil(err)
t.Assert(len(ssp), len(structs))
for k := range structs {
t.Assert(structs[k].Name, ssp[k].Name)
t.Assert(structs[k].Place, ssp[k].Place)
}
}
})
// Test for json converting.
gtest.C(t, func(t *gtest.T) {
scanValuesTest := scanValueJsonTest
for _, test := range scanValuesTest {
var (
err error
scanExpects = scanExpects
)
err = gconv.Scan(test, &scanExpects.mapStrStr)
t.AssertNil(err)
t.Assert("Mars", scanExpects.mapStrStr["Name"])
t.Assert("奥林帕斯山", scanExpects.mapStrStr["Place"])
err = gconv.Scan(test, &scanExpects.mapStrAny)
t.AssertNil(err)
t.Assert("Mars", scanExpects.mapStrAny["Name"])
t.Assert("奥林帕斯山", scanExpects.mapStrAny["Place"])
err = gconv.Scan(test, &scanExpects.mapAnyAny)
t.Assert(err, gerror.New(
"json.UnmarshalUseNumber failed: json: cannot unmarshal object into Go value of type map[interface {}]interface {}",
))
err = gconv.Scan(test, &scanExpects.structSub)
t.AssertNil(err)
t.Assert("Mars", scanExpects.structSub.Name)
t.Assert("奥林帕斯山", scanExpects.structSub.Place)
err = gconv.Scan(test, &scanExpects.structSubPtr)
t.AssertNil(err)
t.Assert("Mars", scanExpects.structSubPtr.Name)
t.Assert("奥林帕斯山", scanExpects.structSubPtr.Place)
}
})
// Test for slice json converting.
gtest.C(t, func(t *gtest.T) {
scanValuesTest := scanValueJsonTest
for _, test := range scanValuesTest {
var (
err error
scanExpects = scanExpects
jsons = fmt.Sprintf("[%s, %s]", test, test)
)
var mss = []map[string]string{scanExpects.mapStrStr, scanExpects.mapStrStr}
err = gconv.Scan(jsons, &mss)
t.AssertNil(err)
t.Assert(len(mss), 2)
for k := range mss {
t.Assert("Mars", mss[k]["Name"])
t.Assert("奥林帕斯山", mss[k]["Place"])
}
var msa = []map[string]any{scanExpects.mapStrAny, scanExpects.mapStrAny}
err = gconv.Scan(jsons, &msa)
t.AssertNil(err)
t.Assert(len(msa), 2)
for k := range msa {
t.Assert("Mars", msa[k]["Name"])
t.Assert("奥林帕斯山", msa[k]["Place"])
}
var maa = []map[any]any{scanExpects.mapAnyAny, scanExpects.mapAnyAny}
err = gconv.Scan(jsons, &maa)
t.Assert(err, gerror.New(
"json.UnmarshalUseNumber failed: json: cannot unmarshal object into Go value of type map[interface {}]interface {}",
))
var ss = []scanStructTest{scanExpects.structSub, scanExpects.structSub}
err = gconv.Scan(jsons, &ss)
t.AssertNil(err)
t.Assert(len(ss), 2)
for k := range ss {
t.Assert("Mars", ss[k].Name)
t.Assert("奥林帕斯山", ss[k].Place)
}
var ssp = []*scanStructTest{scanExpects.structSubPtr, scanExpects.structSubPtr}
err = gconv.Scan(jsons, &ssp)
t.AssertNil(err)
t.Assert(len(ssp), 2)
for k := range ssp {
t.Assert("Mars", ssp[k].Name)
t.Assert("奥林帕斯山", ssp[k].Place)
}
}
})
// Test for paramKeyToAttrMap
gtest.C(t, func(t *gtest.T) {
scanValuesTest := scanValueMapsTest
for _, test := range scanValuesTest {
var (
err error
scanExpects = scanExpects
mapParameter = map[string]string{"Name": "Place", "Place": "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)
}
})
// Test for special types.
gtest.C(t, func(t *gtest.T) {
var (
err error
src = "Sun"
dst = "日冕"
)
err = gconv.Scan(nil, &dst)
t.AssertNil(err)
t.Assert(dst, "日冕")
err = gconv.Scan(src, nil)
t.Assert(err, gerror.New("destination pointer should not be nil"))
// Test for non-pointer.
err = gconv.Scan(src, dst)
t.Assert(err, gerror.New(
"destination pointer should be type of pointer, but got type: string",
))
})
}
func TestScanEmptyStringToCustomType(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
type Status string
type Req struct {
Name string
Statuses []Status
Types []string
}
var (
req *Req
data = g.Map{
"Name": "john",
"Statuses": "",
"Types": "",
}
)
err := gconv.Scan(data, &req)
t.AssertNil(err)
t.Assert(len(req.Statuses), 0)
t.Assert(len(req.Types), 0)
})
}
func TestScanDeepSlice(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var (
req [][]int
req2 [][][]int
data1 = gjson.New("[[1,2,3],[4,5,6]]")
data2 = gjson.New("[[[1,2,3]],[[4,5,6]]]")
)
err := data1.Scan(&req)
t.AssertNil(err)
err = gconv.Scan(data1.String(), &req)
t.AssertNil(err)
err = data2.Scan(&req2)
t.AssertNil(err)
t.Assert(len(req), 2)
t.Assert(len(req2), 2)
})
}