From 15e1ed41f403011d81d1c5172d06fcaf5ec0ff9b Mon Sep 17 00:00:00 2001 From: Yun Date: Tue, 2 Sep 2025 15:43:26 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96any=E8=BD=ACstring?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- auto_conv.go | 2 +- cmd/demo.go | 196 ++++++++++++++++++++++++++++++++++++++++++++++ convx_test.go | 42 +++------- to_string.go | 115 ++++++++++++++++++--------- to_string_test.go | 12 ++- 5 files changed, 297 insertions(+), 70 deletions(-) create mode 100644 cmd/demo.go diff --git a/auto_conv.go b/auto_conv.go index 10170c9..dcdeff3 100644 --- a/auto_conv.go +++ b/auto_conv.go @@ -20,7 +20,7 @@ func AutoConv(toType string, target interface{}) (interface{}, error) { case "int32": v, err = ToInt32(target) case "string": - v, err = ToString(target) + v = ToString(target) } return v, err } diff --git a/cmd/demo.go b/cmd/demo.go new file mode 100644 index 0000000..bd67194 --- /dev/null +++ b/cmd/demo.go @@ -0,0 +1,196 @@ +package main + +import ( + "encoding/json" + "fmt" + "reflect" + "strconv" + "time" +) + +// package main + +// import ( +// "testing" +// "time" +// ) + +// func TestInterfaceToString(t *testing.T) { +// tests := []struct { +// name string +// input interface{} +// want string +// }{ +// {"nil", nil, ""}, +// {"string", "hello", "hello"}, +// {"bytes", []byte("world"), "world"}, +// {"custom stringer", customStringer{}, "custom stringer implementation"}, +// {"error", fmt.Errorf("test error"), "test error"}, +// {"bool true", true, "true"}, +// {"bool false", false, "false"}, +// {"int", 42, "42"}, +// {"int8", int8(8), "8"}, +// {"int16", int16(16), "16"}, +// {"int32", int32(32), "32"}, +// {"int64", int64(64), "64"}, +// {"uint", uint(42), "42"}, +// {"uint8", uint8(8), "8"}, +// {"uint16", uint16(16), "16"}, +// {"uint32", uint32(32), "32"}, +// {"uint64", uint64(64), "64"}, +// {"float32", float32(3.14), "3.14"}, +// {"float64", 3.1415, "3.1415"}, +// {"complex64", complex64(1 + 2i), "(1+2i)"}, +// {"complex128", 1 + 3i, "(1+3i)"}, +// {"time", time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC), "2020-01-01T00:00:00Z"}, +// {"duration", time.Hour, "1h0m0s"}, +// {"slice", []int{1, 2, 3}, "[1,2,3]"}, +// {"array", [2]string{"a", "b"}, `["a","b"]`}, +// {"map", map[string]int{"a": 1}, `{"a":1}`}, +// {"struct", struct{ A int }{42}, `{"A":42}`}, +// {"pointer to int", intPtr(42), "42"}, +// {"pointer to nil", (*int)(nil), ""}, +// {"custom type", TestStr("custom"), "custom"}, +// } + +// for _, tt := range tests { +// t.Run(tt.name, func(t *testing.T) { +// if got := InterfaceToString(tt.input); got != tt.want { +// t.Errorf("InterfaceToString() = %v, want %v", got, tt.want) +// } +// }) +// } +// } + +// func intPtr(i int) *int { +// return &i +// } + + +// InterfaceToString 将任意interface转换为string +func InterfaceToString(v interface{}) string { + if v == nil { + return "" + } + + // 获取值的反射对象 + val := reflect.ValueOf(v) + + // 处理指针类型:解引用直到获取到非指针值 + for val.Kind() == reflect.Ptr { + if val.IsNil() { + return "" + } + val = val.Elem() + } + + // 获取解引用后的实际值 + actualValue := val.Interface() + + // 根据具体类型进行处理 + switch actual := actualValue.(type) { + case string: + return actual + case []byte: + return string(actual) + case fmt.Stringer: + return actual.String() + case error: + return actual.Error() + case bool: + return strconv.FormatBool(actual) + case int: + return strconv.Itoa(actual) + case int8: + return strconv.FormatInt(int64(actual), 10) + case int16: + return strconv.FormatInt(int64(actual), 10) + case int32: + return strconv.FormatInt(int64(actual), 10) + case int64: + return strconv.FormatInt(actual, 10) + case uint: + return strconv.FormatUint(uint64(actual), 10) + case uint8: + return strconv.FormatUint(uint64(actual), 10) + case uint16: + return strconv.FormatUint(uint64(actual), 10) + case uint32: + return strconv.FormatUint(uint64(actual), 10) + case uint64: + return strconv.FormatUint(actual, 10) + case float32: + return strconv.FormatFloat(float64(actual), 'f', -1, 32) + case float64: + return strconv.FormatFloat(actual, 'f', -1, 64) + case complex64: + return fmt.Sprint(actual) + case complex128: + return fmt.Sprint(actual) + case time.Time: + return actual.Format(time.RFC3339) + case time.Duration: + return actual.String() + } + + // 处理切片、数组、map、结构体等复杂类型 - 使用JSON序列化 + if val.IsValid() { + switch val.Kind() { + case reflect.Slice, reflect.Array, reflect.Map, reflect.Struct: + jsonBytes, err := json.Marshal(actualValue) + if err == nil { + return string(jsonBytes) + } + } + } + + // 默认处理:使用fmt.Sprint + return fmt.Sprint(actualValue) +} + +// 示例用法 +func main() { + // 测试各种类型 + testCases := []interface{}{ + nil, + "hello", + 42, + 3.14, + true, + []byte("world"), + []int{1, 2, 3}, + map[string]interface{}{"name": "John", "age": 30}, + struct { + Name string `json:"name"` + Age int `json:"age"` + }{"Alice", 25}, + time.Now(), + time.Hour * 2, + // 指针类型 + &[]string{"a", "b", "c"}, + // 自定义Stringer + customStringer{}, + // 错误类型 + fmt.Errorf("test error"), + TestStrOne, + 2454528425152485425, + } + + for i, tc := range testCases { + result := InterfaceToString(tc) + fmt.Printf("Case %d: %T -> %s\n", i+1, tc, result) + } +} + +// 自定义Stringer实现 +type customStringer struct{} + +func (c customStringer) String() string { + return "custom stringer implementation" +} + +type TestStr string + +const ( + TestStrOne TestStr = "1" +) \ No newline at end of file diff --git a/convx_test.go b/convx_test.go index 0cf9a3c..941290e 100644 --- a/convx_test.go +++ b/convx_test.go @@ -30,11 +30,8 @@ func TestConvToString(t *testing.T) { // 测试int var v_int int = 123456789 - str, err := convx.ToString(v_int) - if err != nil { - t.Fail() - t.Log(err) - } + str := convx.ToString(v_int) + if str != "123456789" { t.Fail() t.Log(str) @@ -42,11 +39,8 @@ func TestConvToString(t *testing.T) { // 测试int32 var v_int32 int32 = 123456789 - str, err = convx.ToString(v_int32) - if err != nil { - t.Fail() - t.Log(err) - } + str = convx.ToString(v_int32) + if str != "123456789" { t.Fail() t.Log(str) @@ -54,11 +48,8 @@ func TestConvToString(t *testing.T) { // 测试int64 var v_int64 int64 = 123456789 - str, err = convx.ToString(v_int64) - if err != nil { - t.Fail() - t.Log(err) - } + str = convx.ToString(v_int64) + if str != "123456789" { t.Fail() t.Log(str) @@ -66,11 +57,8 @@ func TestConvToString(t *testing.T) { // 测试float32 var v_float32 float32 = 123.12345 - str, err = convx.ToString(v_float32) - if err != nil { - t.Fail() - t.Log(err) - } + str = convx.ToString(v_float32) + if str != "123.12345" { t.Fail() t.Log(str) @@ -78,11 +66,8 @@ func TestConvToString(t *testing.T) { // 测试float64 var v_float64 float64 = 123456789.12345 - str, err = convx.ToString(v_float64) - if err != nil { - t.Fail() - t.Log(err) - } + str = convx.ToString(v_float64) + if str != "123456789.12345" { t.Fail() t.Log(str) @@ -90,11 +75,8 @@ func TestConvToString(t *testing.T) { // 测试string var v_string string = "123456789.12345" - str, err = convx.ToString(v_string) - if err != nil { - t.Fail() - t.Log(err) - } + str = convx.ToString(v_string) + if str != "123456789.12345" { t.Fail() t.Log(str) diff --git a/to_string.go b/to_string.go index 33ac932..3ad9cce 100644 --- a/to_string.go +++ b/to_string.go @@ -1,46 +1,91 @@ package convx import ( - "errors" + "encoding/json" "fmt" + "reflect" "strconv" + "time" ) // interface 转 string -func ToString(val interface{}) (str string, err error) { - - switch v := val.(type) { - case nil: - return "", fmt.Errorf("can not convert to string") - case string: - return v, nil - case int: - return strconv.Itoa(v), nil - case int8: - return strconv.FormatInt(int64(v), 10), nil - case int16: - return strconv.FormatInt(int64(v), 10), nil - case int32: - return strconv.FormatInt(int64(v), 10), nil - case int64: - return strconv.FormatInt(v, 10), nil - case float32: - return strconv.FormatFloat(float64(v), 'f', -1, 32), nil - case float64: - return strconv.FormatFloat(v, 'f', -1, 64), nil - case bool: - return strconv.FormatBool(v), nil - case uint: - return strconv.FormatUint(uint64(v), 10), nil - case uint8: - return strconv.FormatUint(uint64(v), 10), nil - case uint16: - return strconv.FormatUint(uint64(v), 10), nil - case uint32: - return strconv.FormatUint(uint64(v), 10), nil - case uint64: - return strconv.FormatUint(v, 10), nil +// InterfaceToString 将任意interface转换为string +func ToString(v interface{}) string { + if v == nil { + return "" } - return "", errors.New("can not convert to string") + // 获取值的反射对象 + val := reflect.ValueOf(v) + + // 处理指针类型:解引用直到获取到非指针值 + for val.Kind() == reflect.Ptr { + if val.IsNil() { + return "" + } + val = val.Elem() + } + + // 获取解引用后的实际值 + actualValue := val.Interface() + + // 根据具体类型进行处理 + switch actual := actualValue.(type) { + case string: + return actual + case []byte: + return string(actual) + case error: + return actual.Error() + case bool: + return strconv.FormatBool(actual) + case int: + return strconv.Itoa(actual) + case int8: + return strconv.FormatInt(int64(actual), 10) + case int16: + return strconv.FormatInt(int64(actual), 10) + case int32: + return strconv.FormatInt(int64(actual), 10) + case int64: + return strconv.FormatInt(actual, 10) + case uint: + return strconv.FormatUint(uint64(actual), 10) + case uint8: + return strconv.FormatUint(uint64(actual), 10) + case uint16: + return strconv.FormatUint(uint64(actual), 10) + case uint32: + return strconv.FormatUint(uint64(actual), 10) + case uint64: + return strconv.FormatUint(actual, 10) + case float32: + return strconv.FormatFloat(float64(actual), 'f', -1, 32) + case float64: + return strconv.FormatFloat(actual, 'f', -1, 64) + case complex64: + return fmt.Sprint(actual) + case complex128: + return fmt.Sprint(actual) + case time.Time: + return actual.Format(time.RFC3339) + case time.Duration: + return actual.String() + case fmt.Stringer: + return actual.String() + } + + // 处理切片、数组、map、结构体等复杂类型 - 使用JSON序列化 + if val.IsValid() { + switch val.Kind() { + case reflect.Slice, reflect.Array, reflect.Map, reflect.Struct: + jsonBytes, err := json.Marshal(actualValue) + if err == nil { + return string(jsonBytes) + } + } + } + + // 默认处理:使用fmt.Sprint + return fmt.Sprint(actualValue) } diff --git a/to_string_test.go b/to_string_test.go index 8f51f8b..e72e9cc 100644 --- a/to_string_test.go +++ b/to_string_test.go @@ -6,6 +6,12 @@ import ( "code.yun.ink/pkg/convx" ) +type TestStr string + +const ( + TestStrOne TestStr = "1" +) + func TestToString(t *testing.T) { var tests = []struct { @@ -26,13 +32,11 @@ func TestToString(t *testing.T) { {uint16(1), "1"}, //uint16 {uint32(1), "1"}, //uint32 {uint64(1), "1"}, //uint64 + {TestStrOne, "1"}, //custom type } for _, test := range tests { - str, err := convx.ToString(test.input) - if err != nil { - t.Errorf("convx.ToString(%v) failed with %v", test.input, err) - } + str := convx.ToString(test.input) if str != test.expect { t.Errorf("convx.ToString(%v) = %v, want %v", test.input, str, test.expect) }