2026-04-22 01:52:30 +08:00
|
|
|
package cast
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"bytes"
|
|
|
|
|
"encoding/json"
|
|
|
|
|
"fmt"
|
|
|
|
|
"reflect"
|
2026-04-30 22:16:01 +08:00
|
|
|
"slices"
|
2026-04-22 01:52:30 +08:00
|
|
|
"strconv"
|
|
|
|
|
"strings"
|
|
|
|
|
"time"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// If 泛型三元表达式
|
2026-04-30 22:16:01 +08:00
|
|
|
func If[T any](condition bool, trueVal, falseVal T) T {
|
|
|
|
|
if condition {
|
|
|
|
|
return trueVal
|
2026-04-22 01:52:30 +08:00
|
|
|
}
|
2026-04-30 22:16:01 +08:00
|
|
|
return falseVal
|
2026-04-22 01:52:30 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// In 泛型包含判断
|
|
|
|
|
func In[T comparable](arr []T, val T) bool {
|
2026-04-30 22:16:01 +08:00
|
|
|
return slices.Contains(arr, val)
|
2026-04-22 01:52:30 +08:00
|
|
|
}
|
|
|
|
|
|
2026-05-04 11:11:37 +08:00
|
|
|
// As 忽略错误,返回零值 (消除摩擦)
|
|
|
|
|
func As[T any](v T, err error) T {
|
2026-05-04 10:52:15 +08:00
|
|
|
if err != nil {
|
|
|
|
|
var zero T
|
|
|
|
|
return zero
|
|
|
|
|
}
|
|
|
|
|
return v
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-22 01:52:30 +08:00
|
|
|
func RealValue(v reflect.Value) reflect.Value {
|
2026-04-30 22:16:01 +08:00
|
|
|
for v.Kind() == reflect.Pointer || v.Kind() == reflect.Interface {
|
|
|
|
|
if v.IsNil() {
|
|
|
|
|
return v
|
|
|
|
|
}
|
2026-04-22 01:52:30 +08:00
|
|
|
v = v.Elem()
|
|
|
|
|
}
|
|
|
|
|
return v
|
|
|
|
|
}
|
2026-05-04 14:10:18 +08:00
|
|
|
|
2026-04-22 01:52:30 +08:00
|
|
|
// --- Core Cast Logic ---
|
|
|
|
|
|
2026-05-04 14:10:18 +08:00
|
|
|
var (
|
|
|
|
|
timeType = reflect.TypeOf(time.Time{})
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// To 深度转换 (支持基础类型、Slice、Map、Struct 及 JSON 自动转换,零摩擦模式)
|
2026-05-04 11:11:37 +08:00
|
|
|
func To[T any](v any) T {
|
2026-05-04 14:10:18 +08:00
|
|
|
var res T
|
|
|
|
|
performRecursiveTo(reflect.ValueOf(v), reflect.ValueOf(&res).Elem())
|
|
|
|
|
return res
|
|
|
|
|
}
|
2026-05-04 10:41:34 +08:00
|
|
|
|
2026-05-04 14:10:18 +08:00
|
|
|
// Convert 深度转换 (支持基础类型、Slice、Map、Struct 及 JSON 自动转换,原地更新)
|
|
|
|
|
func Convert(dst, src any) {
|
|
|
|
|
if dst == nil {
|
|
|
|
|
return
|
2026-05-04 10:41:34 +08:00
|
|
|
}
|
|
|
|
|
|
2026-05-04 14:10:18 +08:00
|
|
|
dstV := reflect.ValueOf(dst)
|
|
|
|
|
var srcV reflect.Value
|
|
|
|
|
if v, ok := src.(reflect.Value); ok {
|
|
|
|
|
srcV = v
|
|
|
|
|
} else {
|
|
|
|
|
srcV = reflect.ValueOf(src)
|
2026-05-04 10:41:34 +08:00
|
|
|
}
|
|
|
|
|
|
2026-05-04 14:10:18 +08:00
|
|
|
// Task 1: 拦截“自我转换”死循环陷阱 (Self-Assignment Guard)
|
|
|
|
|
if dstV.Kind() == reflect.Pointer && srcV.Kind() == reflect.Pointer {
|
|
|
|
|
if dstV.Pointer() == srcV.Pointer() {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
performRecursiveTo(srcV, dstV)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func performRecursiveTo(src, dst reflect.Value) {
|
|
|
|
|
// 0. 处理 Interface 目标 (直接赋值)
|
|
|
|
|
if dst.Kind() == reflect.Interface {
|
|
|
|
|
if dst.CanSet() {
|
|
|
|
|
dst.Set(RealValue(src))
|
|
|
|
|
}
|
|
|
|
|
return
|
2026-05-04 10:41:34 +08:00
|
|
|
}
|
2026-05-04 14:10:18 +08:00
|
|
|
|
|
|
|
|
// 1. 处理指针 (自动初始化与解引用)
|
|
|
|
|
for dst.Kind() == reflect.Pointer {
|
|
|
|
|
if dst.IsNil() {
|
|
|
|
|
if dst.CanSet() {
|
|
|
|
|
dst.Set(reflect.New(dst.Type().Elem()))
|
|
|
|
|
} else {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
dst = dst.Elem()
|
2026-05-04 10:41:34 +08:00
|
|
|
}
|
|
|
|
|
|
2026-05-04 14:10:18 +08:00
|
|
|
// 2. 解开源数据
|
|
|
|
|
src = RealValue(src)
|
|
|
|
|
if !src.IsValid() {
|
|
|
|
|
return
|
2026-05-04 10:41:34 +08:00
|
|
|
}
|
|
|
|
|
|
2026-05-04 14:10:18 +08:00
|
|
|
dstType := dst.Type()
|
|
|
|
|
|
|
|
|
|
// 3. 处理 JSON Unmarshaler 接口 (优先级高)
|
|
|
|
|
if dst.CanAddr() {
|
|
|
|
|
if unmarshaler, ok := dst.Addr().Interface().(json.Unmarshaler); ok {
|
|
|
|
|
var b []byte
|
|
|
|
|
if src.Kind() == reflect.String {
|
|
|
|
|
b = []byte(src.String())
|
|
|
|
|
} else if src.Kind() == reflect.Slice && src.Type().Elem().Kind() == reflect.Uint8 {
|
|
|
|
|
b = src.Bytes()
|
|
|
|
|
} else {
|
|
|
|
|
b, _ = ToJSONBytes(src.Interface())
|
|
|
|
|
}
|
|
|
|
|
if len(b) > 0 {
|
|
|
|
|
_ = unmarshaler.UnmarshalJSON(b)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 4. Frictionless 增强:单值 <-> 切片 自动转换
|
|
|
|
|
if src.Kind() == reflect.Slice && src.Type().Elem().Kind() != reflect.Uint8 && src.Len() > 0 &&
|
|
|
|
|
dst.Kind() != reflect.Slice && dst.Kind() != reflect.Array && dst.Kind() != reflect.Map {
|
|
|
|
|
src = RealValue(src.Index(0))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 5. 基础类型快速转换 (利用 cast 已有的高性能基础函数)
|
|
|
|
|
switch dst.Kind() {
|
2026-05-04 10:52:15 +08:00
|
|
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
2026-05-04 14:10:18 +08:00
|
|
|
if dst.CanSet() {
|
|
|
|
|
dst.SetInt(Int64(src.Interface()))
|
2026-05-04 10:52:15 +08:00
|
|
|
}
|
2026-05-04 14:10:18 +08:00
|
|
|
return
|
|
|
|
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
|
|
|
|
if dst.CanSet() {
|
|
|
|
|
dst.SetUint(Uint64(src.Interface()))
|
2026-05-04 10:52:15 +08:00
|
|
|
}
|
2026-05-04 14:10:18 +08:00
|
|
|
return
|
2026-05-04 10:52:15 +08:00
|
|
|
case reflect.Float32, reflect.Float64:
|
2026-05-04 14:10:18 +08:00
|
|
|
if dst.CanSet() {
|
|
|
|
|
dst.SetFloat(Float64(src.Interface()))
|
2026-05-04 10:52:15 +08:00
|
|
|
}
|
2026-05-04 14:10:18 +08:00
|
|
|
return
|
2026-05-04 10:52:15 +08:00
|
|
|
case reflect.String:
|
2026-05-04 14:10:18 +08:00
|
|
|
if dst.CanSet() {
|
|
|
|
|
if isComplexValue(src.Interface()) {
|
|
|
|
|
s, _ := ToJSON(src.Interface())
|
|
|
|
|
dst.SetString(s)
|
|
|
|
|
} else {
|
|
|
|
|
dst.SetString(String(src.Interface()))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return
|
2026-05-04 10:52:15 +08:00
|
|
|
case reflect.Bool:
|
2026-05-04 14:10:18 +08:00
|
|
|
if dst.CanSet() {
|
|
|
|
|
dst.SetBool(Bool(src.Interface()))
|
2026-05-04 10:52:15 +08:00
|
|
|
}
|
2026-05-04 14:10:18 +08:00
|
|
|
return
|
2026-05-04 10:52:15 +08:00
|
|
|
}
|
|
|
|
|
|
2026-05-04 14:10:18 +08:00
|
|
|
// 原生 time.Time 支持
|
|
|
|
|
if dstType == timeType {
|
|
|
|
|
if dst.CanSet() {
|
|
|
|
|
dst.Set(reflect.ValueOf(ToTime(src.Interface(), "")))
|
|
|
|
|
}
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 6. 复杂容器深度递归转换
|
|
|
|
|
switch dst.Kind() {
|
|
|
|
|
case reflect.Struct:
|
|
|
|
|
if src.Kind() == reflect.Map {
|
|
|
|
|
recursiveMapToStruct(src, dst)
|
|
|
|
|
} else if src.Kind() == reflect.Struct {
|
|
|
|
|
recursiveStructToStruct(src, dst)
|
|
|
|
|
} else if src.Kind() == reflect.String || (src.Kind() == reflect.Slice && src.Type().Elem().Kind() == reflect.Uint8) {
|
|
|
|
|
if dst.CanAddr() {
|
|
|
|
|
_ = UnmarshalJSON(src.Interface(), dst.Addr().Interface())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
case reflect.Map:
|
|
|
|
|
if dst.IsNil() {
|
|
|
|
|
if dst.CanSet() {
|
|
|
|
|
dst.Set(reflect.MakeMap(dstType))
|
|
|
|
|
} else {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if src.Kind() == reflect.Map {
|
|
|
|
|
recursiveMapToMap(src, dst)
|
|
|
|
|
} else if src.Kind() == reflect.Struct {
|
|
|
|
|
recursiveStructToMap(src, dst)
|
|
|
|
|
} else if src.Kind() == reflect.Slice || src.Kind() == reflect.Array {
|
|
|
|
|
recursiveSliceToMap(src, dst)
|
|
|
|
|
} else if src.Kind() == reflect.String {
|
|
|
|
|
if dst.CanAddr() {
|
|
|
|
|
_ = UnmarshalJSON(src.Interface(), dst.Addr().Interface())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
case reflect.Slice:
|
|
|
|
|
if dstType.Elem().Kind() == reflect.Uint8 {
|
|
|
|
|
if dst.CanSet() {
|
|
|
|
|
if isComplexValue(src.Interface()) {
|
|
|
|
|
b, _ := ToJSONBytes(src.Interface())
|
|
|
|
|
dst.SetBytes(b)
|
|
|
|
|
} else {
|
|
|
|
|
dst.SetBytes(toBytes(src.Interface()))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// JSON/CSV 字符串增强
|
|
|
|
|
if src.Kind() == reflect.String {
|
|
|
|
|
s := src.String()
|
|
|
|
|
if isJSONText(s) {
|
|
|
|
|
if dst.CanAddr() {
|
|
|
|
|
_ = UnmarshalJSON(s, dst.Addr().Interface())
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if !strings.HasPrefix(s, "[") && strings.Contains(s, ",") {
|
|
|
|
|
src = reflect.ValueOf(Split(s, ","))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
recursiveSliceToSlice(src, dst)
|
|
|
|
|
}
|
|
|
|
|
case reflect.Func:
|
|
|
|
|
if src.Kind() == reflect.Func {
|
|
|
|
|
dst.Set(reflect.MakeFunc(dstType, func(args []reflect.Value) []reflect.Value {
|
|
|
|
|
srcType := src.Type()
|
|
|
|
|
inParams := make([]reflect.Value, srcType.NumIn())
|
|
|
|
|
for i := 0; i < srcType.NumIn(); i++ {
|
|
|
|
|
inParams[i] = reflect.New(srcType.In(i)).Elem()
|
|
|
|
|
if i < len(args) {
|
|
|
|
|
performRecursiveTo(args[i], inParams[i])
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
results := src.Call(inParams)
|
|
|
|
|
outParams := make([]reflect.Value, dstType.NumOut())
|
|
|
|
|
for i := 0; i < dstType.NumOut(); i++ {
|
|
|
|
|
outParams[i] = reflect.New(dstType.Out(i)).Elem()
|
|
|
|
|
if i < len(results) {
|
|
|
|
|
performRecursiveTo(results[i], outParams[i])
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return outParams
|
|
|
|
|
}))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 内部递归助手 (零分配/高性能设计)
|
|
|
|
|
|
|
|
|
|
func recursiveMapToStruct(src, dst reflect.Value) {
|
|
|
|
|
if !dst.CanAddr() {
|
|
|
|
|
return
|
2026-05-04 10:52:15 +08:00
|
|
|
}
|
2026-05-04 14:10:18 +08:00
|
|
|
dstAddr := dst.Addr()
|
|
|
|
|
dstType := dst.Type()
|
|
|
|
|
descriptor := getDecoderFieldMap(dstType)
|
|
|
|
|
iter := src.MapRange() // 零分配遍历
|
|
|
|
|
for iter.Next() {
|
|
|
|
|
rk := iter.Key()
|
|
|
|
|
var key string
|
|
|
|
|
if rk.Kind() == reflect.String {
|
|
|
|
|
key = rk.String()
|
|
|
|
|
} else {
|
|
|
|
|
key = String(rk.Interface())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if index, isTime, format, ok := matchField(key, descriptor); ok {
|
|
|
|
|
field := dst.Field(index)
|
|
|
|
|
fieldInfo := dstType.Field(index)
|
|
|
|
|
|
|
|
|
|
// ParseHook 支持: ParseFieldName(sourceType) FieldType
|
|
|
|
|
if method, ok := dstAddr.Type().MethodByName("Parse" + fieldInfo.Name); ok {
|
|
|
|
|
arg := reflect.New(method.Type.In(1)).Elem()
|
|
|
|
|
performRecursiveTo(iter.Value(), arg)
|
|
|
|
|
res := method.Func.Call([]reflect.Value{dstAddr, arg})
|
|
|
|
|
field.Set(res[0])
|
|
|
|
|
continue
|
|
|
|
|
}
|
2026-05-04 10:52:15 +08:00
|
|
|
|
2026-05-04 14:10:18 +08:00
|
|
|
if isTime {
|
|
|
|
|
field.Set(reflect.ValueOf(ToTime(iter.Value().Interface(), format)))
|
|
|
|
|
} else {
|
|
|
|
|
performRecursiveTo(iter.Value(), field)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-05-04 10:52:15 +08:00
|
|
|
}
|
|
|
|
|
|
2026-05-04 14:10:18 +08:00
|
|
|
func recursiveStructToStruct(src, dst reflect.Value) {
|
|
|
|
|
if !dst.CanAddr() {
|
|
|
|
|
return
|
2026-05-04 10:52:15 +08:00
|
|
|
}
|
2026-05-04 14:10:18 +08:00
|
|
|
dstAddr := dst.Addr()
|
|
|
|
|
srcType := src.Type()
|
|
|
|
|
dstType := dst.Type()
|
|
|
|
|
descriptor := getDecoderFieldMap(dstType)
|
|
|
|
|
for i := 0; i < src.NumField(); i++ {
|
|
|
|
|
field := srcType.Field(i)
|
|
|
|
|
if !field.IsExported() {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
if field.Anonymous {
|
|
|
|
|
recursiveStructToStruct(src.Field(i), dst)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
// 使用 matchField 实现归一化 Key 匹配 (零分配)
|
|
|
|
|
if index, isTime, format, ok := matchField(field.Name, descriptor); ok {
|
|
|
|
|
df := dst.Field(index)
|
|
|
|
|
fieldInfo := dstType.Field(index)
|
|
|
|
|
|
|
|
|
|
// ParseHook 支持
|
|
|
|
|
if method, ok := dstAddr.Type().MethodByName("Parse" + fieldInfo.Name); ok {
|
|
|
|
|
arg := reflect.New(method.Type.In(1)).Elem()
|
|
|
|
|
performRecursiveTo(src.Field(i), arg)
|
|
|
|
|
res := method.Func.Call([]reflect.Value{dstAddr, arg})
|
|
|
|
|
df.Set(res[0])
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if isTime {
|
|
|
|
|
df.Set(reflect.ValueOf(ToTime(src.Field(i).Interface(), format)))
|
|
|
|
|
} else {
|
|
|
|
|
performRecursiveTo(src.Field(i), df)
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-05-04 10:52:15 +08:00
|
|
|
}
|
2026-05-04 14:10:18 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func recursiveMapToMap(src, dst reflect.Value) {
|
|
|
|
|
kt := dst.Type().Key()
|
|
|
|
|
vt := dst.Type().Elem()
|
|
|
|
|
newKey := reflect.New(kt).Elem()
|
|
|
|
|
newVal := reflect.New(vt).Elem()
|
|
|
|
|
iter := src.MapRange()
|
|
|
|
|
for iter.Next() {
|
|
|
|
|
newKey.Set(reflect.Zero(kt))
|
|
|
|
|
performRecursiveTo(iter.Key(), newKey)
|
|
|
|
|
newVal.Set(reflect.Zero(vt))
|
|
|
|
|
performRecursiveTo(iter.Value(), newVal)
|
|
|
|
|
dst.SetMapIndex(newKey, newVal)
|
2026-05-04 10:52:15 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-04 14:10:18 +08:00
|
|
|
func recursiveStructToMap(src, dst reflect.Value) {
|
|
|
|
|
kt := dst.Type().Key()
|
|
|
|
|
vt := dst.Type().Elem()
|
|
|
|
|
newKey := reflect.New(kt).Elem()
|
|
|
|
|
newVal := reflect.New(vt).Elem()
|
|
|
|
|
srcType := src.Type()
|
|
|
|
|
for i := 0; i < src.NumField(); i++ {
|
|
|
|
|
field := srcType.Field(i)
|
|
|
|
|
if !field.IsExported() {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
if field.Anonymous {
|
|
|
|
|
recursiveStructToMap(src.Field(i), dst)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
newKey.Set(reflect.Zero(kt))
|
|
|
|
|
performRecursiveTo(reflect.ValueOf(GetLowerName(field.Name)), newKey)
|
|
|
|
|
newVal.Set(reflect.Zero(vt))
|
|
|
|
|
performRecursiveTo(src.Field(i), newVal)
|
|
|
|
|
dst.SetMapIndex(newKey, newVal)
|
2026-05-04 10:52:15 +08:00
|
|
|
}
|
2026-05-04 14:10:18 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func recursiveSliceToMap(src, dst reflect.Value) {
|
|
|
|
|
kt := dst.Type().Key()
|
|
|
|
|
vt := dst.Type().Elem()
|
|
|
|
|
newKey := reflect.New(kt).Elem()
|
|
|
|
|
newVal := reflect.New(vt).Elem()
|
|
|
|
|
for i := 0; i < src.Len(); i += 2 {
|
|
|
|
|
newKey.Set(reflect.Zero(kt))
|
|
|
|
|
performRecursiveTo(src.Index(i), newKey)
|
|
|
|
|
newVal.Set(reflect.Zero(vt))
|
|
|
|
|
if i+1 < src.Len() {
|
|
|
|
|
performRecursiveTo(src.Index(i+1), newVal)
|
|
|
|
|
}
|
|
|
|
|
dst.SetMapIndex(newKey, newVal)
|
2026-05-04 10:52:15 +08:00
|
|
|
}
|
2026-05-04 14:10:18 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func recursiveSliceToSlice(src, dst reflect.Value) {
|
|
|
|
|
et := dst.Type().Elem()
|
|
|
|
|
temp := reflect.New(et).Elem()
|
|
|
|
|
if src.Kind() == reflect.Slice || src.Kind() == reflect.Array {
|
|
|
|
|
l := src.Len()
|
|
|
|
|
dst.Set(reflect.MakeSlice(dst.Type(), l, l))
|
|
|
|
|
for i := 0; i < l; i++ {
|
|
|
|
|
temp.Set(reflect.Zero(et))
|
|
|
|
|
performRecursiveTo(src.Index(i), temp)
|
|
|
|
|
dst.Index(i).Set(temp)
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// Frictionless: 自动包装单值为切片 (覆盖赋值)
|
|
|
|
|
dst.Set(reflect.MakeSlice(dst.Type(), 1, 1))
|
|
|
|
|
temp.Set(reflect.Zero(et))
|
|
|
|
|
performRecursiveTo(src, temp)
|
|
|
|
|
dst.Index(0).Set(temp)
|
2026-05-04 10:52:15 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-04 14:10:18 +08:00
|
|
|
func ToTime(v any, format string) time.Time {
|
|
|
|
|
if v == nil {
|
|
|
|
|
return time.Time{}
|
2026-05-04 10:52:15 +08:00
|
|
|
}
|
2026-05-04 14:10:18 +08:00
|
|
|
if t, ok := v.(time.Time); ok {
|
|
|
|
|
return t.In(DefaultTimeZone.loc)
|
|
|
|
|
}
|
|
|
|
|
if format != "" {
|
|
|
|
|
s := String(v)
|
|
|
|
|
if t, err := time.ParseInLocation(format, s, DefaultTimeZone.loc); err == nil {
|
|
|
|
|
return t
|
|
|
|
|
}
|
2026-05-04 10:52:15 +08:00
|
|
|
}
|
2026-05-04 14:10:18 +08:00
|
|
|
return DefaultTimeZone.ParseTime(v)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func reflectCast(value any, t reflect.Type) reflect.Value {
|
|
|
|
|
var v reflect.Value
|
|
|
|
|
if t.Kind() == reflect.Ptr {
|
|
|
|
|
v = reflect.New(t.Elem())
|
|
|
|
|
} else {
|
|
|
|
|
v = reflect.New(t).Elem()
|
|
|
|
|
}
|
|
|
|
|
performRecursiveTo(reflect.ValueOf(value), v)
|
|
|
|
|
return v
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func reflectCastE(value any, t reflect.Type) (reflect.Value, error) {
|
|
|
|
|
return reflectCast(value, t), nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func ToInt64E(v any) (int64, error) {
|
|
|
|
|
return Int64(v), nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func ToUint64E(v any) (uint64, error) {
|
|
|
|
|
return Uint64(v), nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func ToFloat64E(v any) (float64, error) {
|
|
|
|
|
return Float64(v), nil
|
2026-05-04 10:52:15 +08:00
|
|
|
}
|
|
|
|
|
|
2026-05-04 10:41:34 +08:00
|
|
|
func isJSONText(v any) bool {
|
|
|
|
|
switch val := v.(type) {
|
|
|
|
|
case string:
|
|
|
|
|
s := strings.TrimSpace(val)
|
|
|
|
|
return strings.HasPrefix(s, "{") || strings.HasPrefix(s, "[")
|
|
|
|
|
case []byte:
|
|
|
|
|
s := bytes.TrimSpace(val)
|
|
|
|
|
return bytes.HasPrefix(s, []byte("{")) || bytes.HasPrefix(s, []byte("["))
|
|
|
|
|
}
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func isComplexType(t reflect.Type) bool {
|
|
|
|
|
kind := t.Kind()
|
|
|
|
|
for kind == reflect.Ptr {
|
|
|
|
|
t = t.Elem()
|
|
|
|
|
kind = t.Kind()
|
|
|
|
|
}
|
|
|
|
|
return kind == reflect.Struct || kind == reflect.Map || (kind == reflect.Slice && t.Elem().Kind() != reflect.Uint8)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func isComplexValue(v any) bool {
|
|
|
|
|
if v == nil {
|
2026-05-04 10:52:15 +08:00
|
|
|
return true
|
2026-05-04 10:41:34 +08:00
|
|
|
}
|
|
|
|
|
rv := RealValue(reflect.ValueOf(v))
|
2026-05-04 10:52:15 +08:00
|
|
|
if !rv.IsValid() || rv.Kind() == reflect.Pointer || rv.Kind() == reflect.Interface {
|
|
|
|
|
return true
|
2026-05-04 10:41:34 +08:00
|
|
|
}
|
|
|
|
|
kind := rv.Kind()
|
|
|
|
|
return kind == reflect.Struct || kind == reflect.Map || (kind == reflect.Slice && rv.Type().Elem().Kind() != reflect.Uint8)
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-30 22:16:01 +08:00
|
|
|
func parseInt(s string) int64 {
|
2026-04-22 01:52:30 +08:00
|
|
|
i, err := strconv.ParseInt(s, 10, 64)
|
|
|
|
|
if err == nil {
|
|
|
|
|
return i
|
|
|
|
|
}
|
2026-04-30 22:16:01 +08:00
|
|
|
if f, err := strconv.ParseFloat(s, 64); err == nil {
|
|
|
|
|
return int64(f)
|
|
|
|
|
}
|
2026-04-22 01:52:30 +08:00
|
|
|
return 0
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-30 22:16:01 +08:00
|
|
|
func parseUint(s string) uint64 {
|
2026-04-22 01:52:30 +08:00
|
|
|
i, err := strconv.ParseUint(s, 10, 64)
|
|
|
|
|
if err == nil {
|
|
|
|
|
return i
|
|
|
|
|
}
|
2026-04-30 22:16:01 +08:00
|
|
|
if f, err := strconv.ParseFloat(s, 64); err == nil {
|
|
|
|
|
return uint64(f)
|
|
|
|
|
}
|
2026-04-22 01:52:30 +08:00
|
|
|
return 0
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-30 22:16:01 +08:00
|
|
|
func Int(value any) int { return int(Int64(value)) }
|
2026-04-22 01:52:30 +08:00
|
|
|
|
2026-05-01 00:41:23 +08:00
|
|
|
// Helper for integer coercion to avoid repetition
|
|
|
|
|
func toInt64(value any) (int64, bool) {
|
|
|
|
|
switch v := value.(type) {
|
2026-05-04 14:10:18 +08:00
|
|
|
case int:
|
|
|
|
|
return int64(v), true
|
|
|
|
|
case int8:
|
|
|
|
|
return int64(v), true
|
|
|
|
|
case int16:
|
|
|
|
|
return int64(v), true
|
|
|
|
|
case int32:
|
|
|
|
|
return int64(v), true
|
|
|
|
|
case int64:
|
|
|
|
|
return v, true
|
|
|
|
|
case uint:
|
|
|
|
|
return int64(v), true
|
|
|
|
|
case uint8:
|
|
|
|
|
return int64(v), true
|
|
|
|
|
case uint16:
|
|
|
|
|
return int64(v), true
|
|
|
|
|
case uint32:
|
|
|
|
|
return int64(v), true
|
|
|
|
|
case uint64:
|
|
|
|
|
return int64(v), true
|
|
|
|
|
case float32:
|
|
|
|
|
return int64(v), true
|
|
|
|
|
case float64:
|
|
|
|
|
return int64(v), true
|
|
|
|
|
case bool:
|
|
|
|
|
return If(v, int64(1), int64(0)), true
|
2026-05-01 00:41:23 +08:00
|
|
|
}
|
|
|
|
|
return 0, false
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-30 22:16:01 +08:00
|
|
|
func Int64(value any) int64 {
|
2026-05-04 14:10:18 +08:00
|
|
|
if value == nil {
|
|
|
|
|
return 0
|
|
|
|
|
}
|
|
|
|
|
if i, ok := toInt64(value); ok {
|
|
|
|
|
return i
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-22 01:52:30 +08:00
|
|
|
switch realValue := value.(type) {
|
2026-05-04 14:10:18 +08:00
|
|
|
case []byte:
|
|
|
|
|
return parseInt(string(realValue))
|
|
|
|
|
case string:
|
|
|
|
|
return parseInt(realValue)
|
2026-04-30 22:16:01 +08:00
|
|
|
}
|
2026-05-04 14:10:18 +08:00
|
|
|
|
2026-04-30 22:16:01 +08:00
|
|
|
rv := reflect.ValueOf(value)
|
|
|
|
|
if rv.Kind() == reflect.Pointer || rv.Kind() == reflect.Interface {
|
|
|
|
|
if rv = RealValue(rv); rv.IsValid() && rv.CanInterface() && rv.Kind() != reflect.Pointer {
|
|
|
|
|
return Int64(rv.Interface())
|
|
|
|
|
}
|
2026-04-22 01:52:30 +08:00
|
|
|
}
|
|
|
|
|
return 0
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-30 22:16:01 +08:00
|
|
|
func Uint(value any) uint { return uint(Uint64(value)) }
|
2026-04-22 01:52:30 +08:00
|
|
|
|
2026-04-30 22:16:01 +08:00
|
|
|
func Uint64(value any) uint64 {
|
2026-04-22 01:52:30 +08:00
|
|
|
if value == nil {
|
|
|
|
|
return 0
|
|
|
|
|
}
|
|
|
|
|
switch realValue := value.(type) {
|
2026-04-30 22:16:01 +08:00
|
|
|
case int:
|
|
|
|
|
return uint64(realValue)
|
|
|
|
|
case int8:
|
|
|
|
|
return uint64(realValue)
|
|
|
|
|
case int16:
|
|
|
|
|
return uint64(realValue)
|
|
|
|
|
case int32:
|
|
|
|
|
return uint64(realValue)
|
|
|
|
|
case int64:
|
|
|
|
|
return uint64(realValue)
|
|
|
|
|
case uint:
|
|
|
|
|
return uint64(realValue)
|
|
|
|
|
case uint8:
|
|
|
|
|
return uint64(realValue)
|
|
|
|
|
case uint16:
|
|
|
|
|
return uint64(realValue)
|
|
|
|
|
case uint32:
|
|
|
|
|
return uint64(realValue)
|
|
|
|
|
case uint64:
|
|
|
|
|
return realValue
|
|
|
|
|
case float32:
|
|
|
|
|
return uint64(realValue)
|
|
|
|
|
case float64:
|
|
|
|
|
return uint64(realValue)
|
|
|
|
|
case bool:
|
|
|
|
|
return If(realValue, uint64(1), uint64(0))
|
|
|
|
|
case []byte:
|
|
|
|
|
return parseUint(string(realValue))
|
|
|
|
|
case string:
|
|
|
|
|
return parseUint(realValue)
|
|
|
|
|
}
|
|
|
|
|
rv := reflect.ValueOf(value)
|
|
|
|
|
if rv.Kind() == reflect.Pointer || rv.Kind() == reflect.Interface {
|
|
|
|
|
if rv = RealValue(rv); rv.IsValid() && rv.CanInterface() && rv.Kind() != reflect.Pointer {
|
|
|
|
|
return Uint64(rv.Interface())
|
|
|
|
|
}
|
2026-04-22 01:52:30 +08:00
|
|
|
}
|
|
|
|
|
return 0
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-30 22:16:01 +08:00
|
|
|
func Float(value any) float32 { return float32(Float64(value)) }
|
2026-04-22 01:52:30 +08:00
|
|
|
|
2026-04-30 22:16:01 +08:00
|
|
|
func Float64(value any) float64 {
|
2026-04-22 01:52:30 +08:00
|
|
|
if value == nil {
|
|
|
|
|
return 0
|
|
|
|
|
}
|
|
|
|
|
switch realValue := value.(type) {
|
2026-04-30 22:16:01 +08:00
|
|
|
case int, int8, int16, int32, int64:
|
|
|
|
|
return float64(Int64(realValue))
|
|
|
|
|
case uint, uint8, uint16, uint32, uint64:
|
|
|
|
|
return float64(Uint64(realValue))
|
|
|
|
|
case float32:
|
|
|
|
|
return float64(realValue)
|
|
|
|
|
case float64:
|
|
|
|
|
return realValue
|
|
|
|
|
case bool:
|
|
|
|
|
return If(realValue, 1.0, 0.0)
|
2026-04-22 01:52:30 +08:00
|
|
|
case []byte:
|
|
|
|
|
i, err := strconv.ParseFloat(string(realValue), 64)
|
2026-04-30 22:16:01 +08:00
|
|
|
if err == nil {
|
|
|
|
|
return i
|
|
|
|
|
}
|
2026-04-22 01:52:30 +08:00
|
|
|
case string:
|
|
|
|
|
i, err := strconv.ParseFloat(realValue, 64)
|
2026-04-30 22:16:01 +08:00
|
|
|
if err == nil {
|
|
|
|
|
return i
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
rv := reflect.ValueOf(value)
|
|
|
|
|
if rv.Kind() == reflect.Pointer || rv.Kind() == reflect.Interface {
|
|
|
|
|
if rv = RealValue(rv); rv.IsValid() && rv.CanInterface() && rv.Kind() != reflect.Pointer {
|
|
|
|
|
return Float64(rv.Interface())
|
|
|
|
|
}
|
2026-04-22 01:52:30 +08:00
|
|
|
}
|
|
|
|
|
return 0
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-30 22:16:01 +08:00
|
|
|
func String(value any) string {
|
|
|
|
|
if value == nil {
|
|
|
|
|
return ""
|
|
|
|
|
}
|
2026-04-22 01:52:30 +08:00
|
|
|
switch realValue := value.(type) {
|
2026-04-30 22:16:01 +08:00
|
|
|
case int, int8, int16, int32, int64:
|
|
|
|
|
return strconv.FormatInt(Int64(realValue), 10)
|
|
|
|
|
case uint, uint8, uint16, uint32, uint64:
|
|
|
|
|
return strconv.FormatUint(Uint64(realValue), 10)
|
|
|
|
|
case float32:
|
|
|
|
|
return strconv.FormatFloat(float64(realValue), 'f', -1, 32)
|
|
|
|
|
case float64:
|
|
|
|
|
return strconv.FormatFloat(realValue, 'f', -1, 64)
|
|
|
|
|
case bool:
|
|
|
|
|
return If(realValue, "true", "false")
|
|
|
|
|
case string:
|
|
|
|
|
return realValue
|
|
|
|
|
case []byte:
|
|
|
|
|
return string(realValue)
|
|
|
|
|
}
|
|
|
|
|
rv := reflect.ValueOf(value)
|
|
|
|
|
if rv.Kind() == reflect.Pointer || rv.Kind() == reflect.Interface {
|
|
|
|
|
if rv = RealValue(rv); rv.IsValid() && rv.CanInterface() && rv.Kind() != reflect.Pointer {
|
|
|
|
|
return String(rv.Interface())
|
|
|
|
|
}
|
2026-04-22 01:52:30 +08:00
|
|
|
}
|
|
|
|
|
return fmt.Sprint(value)
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-30 22:16:01 +08:00
|
|
|
func Bool(value any) bool {
|
2026-05-04 14:10:18 +08:00
|
|
|
if value == nil {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
if b, ok := value.(bool); ok {
|
|
|
|
|
return b
|
|
|
|
|
}
|
|
|
|
|
if i, ok := toInt64(value); ok {
|
|
|
|
|
return i != 0
|
|
|
|
|
}
|
2026-04-22 01:52:30 +08:00
|
|
|
switch realValue := value.(type) {
|
|
|
|
|
case []byte:
|
|
|
|
|
s := strings.ToLower(string(realValue))
|
|
|
|
|
return s == "1" || s == "t" || s == "true"
|
|
|
|
|
case string:
|
|
|
|
|
s := strings.ToLower(realValue)
|
|
|
|
|
return s == "1" || s == "t" || s == "true"
|
|
|
|
|
}
|
2026-04-30 22:16:01 +08:00
|
|
|
rv := reflect.ValueOf(value)
|
|
|
|
|
if rv.Kind() == reflect.Pointer || rv.Kind() == reflect.Interface {
|
|
|
|
|
if rv = RealValue(rv); rv.IsValid() && rv.CanInterface() && rv.Kind() != reflect.Pointer {
|
|
|
|
|
return Bool(rv.Interface())
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-04-22 01:52:30 +08:00
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-30 22:16:01 +08:00
|
|
|
func Duration(value any) time.Duration {
|
|
|
|
|
if value == nil {
|
|
|
|
|
return 0
|
|
|
|
|
}
|
|
|
|
|
switch realValue := value.(type) {
|
|
|
|
|
case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
|
|
|
|
|
return time.Duration(Int64(realValue))
|
|
|
|
|
case float32, float64:
|
|
|
|
|
return time.Duration(Float64(realValue) * float64(time.Second))
|
|
|
|
|
case []byte, string:
|
|
|
|
|
if result, err := time.ParseDuration(String(realValue)); err == nil {
|
|
|
|
|
return result
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
rv := reflect.ValueOf(value)
|
|
|
|
|
if rv.Kind() == reflect.Pointer || rv.Kind() == reflect.Interface {
|
|
|
|
|
if rv = RealValue(rv); rv.IsValid() && rv.CanInterface() && rv.Kind() != reflect.Pointer {
|
|
|
|
|
return Duration(rv.Interface())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return 0
|
|
|
|
|
}
|
2026-04-22 01:52:30 +08:00
|
|
|
|
2026-05-04 11:24:07 +08:00
|
|
|
func ToJSONBytes(value any) ([]byte, error) {
|
|
|
|
|
return fastToJSONBytes(value)
|
2026-05-02 23:00:44 +08:00
|
|
|
}
|
|
|
|
|
|
2026-05-04 11:24:07 +08:00
|
|
|
func ToJSONDesensitizeBytes(value any, keys []string) ([]byte, error) {
|
|
|
|
|
return fastToJSONBytes(value, keys...)
|
2026-05-02 23:00:44 +08:00
|
|
|
}
|
|
|
|
|
|
2026-05-04 11:24:07 +08:00
|
|
|
func ToJSON(value any) (string, error) {
|
|
|
|
|
b, err := ToJSONBytes(value)
|
|
|
|
|
return string(b), err
|
2026-04-22 01:52:30 +08:00
|
|
|
}
|
|
|
|
|
|
2026-05-04 11:11:37 +08:00
|
|
|
func PrettyToJSON(value any) string {
|
2026-05-04 11:24:07 +08:00
|
|
|
j := As(ToJSONBytes(value))
|
2026-04-22 01:52:30 +08:00
|
|
|
r := &bytes.Buffer{}
|
|
|
|
|
if err := json.Indent(r, j, "", " "); err == nil {
|
2026-05-04 11:11:37 +08:00
|
|
|
return r.String()
|
2026-04-22 01:52:30 +08:00
|
|
|
}
|
2026-05-04 11:11:37 +08:00
|
|
|
return string(j)
|
2026-04-22 01:52:30 +08:00
|
|
|
}
|
|
|
|
|
|
2026-05-04 09:29:06 +08:00
|
|
|
func toBytes(data any) []byte {
|
|
|
|
|
if data == nil {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
2026-05-04 09:22:34 +08:00
|
|
|
switch v := data.(type) {
|
|
|
|
|
case []byte:
|
2026-05-04 09:29:06 +08:00
|
|
|
return v
|
|
|
|
|
case string:
|
|
|
|
|
return []byte(v)
|
|
|
|
|
}
|
|
|
|
|
return []byte(String(data))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func UnmarshalJSON(data any, value any) error {
|
|
|
|
|
b := toBytes(data)
|
|
|
|
|
if b == nil {
|
|
|
|
|
return fmt.Errorf("nil data")
|
2026-04-22 01:52:30 +08:00
|
|
|
}
|
2026-05-04 09:22:34 +08:00
|
|
|
return fastUnmarshalJSONBytes(b, value)
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-04 11:24:07 +08:00
|
|
|
func FromJSON[T any](data any) (T, error) {
|
2026-05-04 09:22:34 +08:00
|
|
|
var v T
|
2026-05-04 11:24:07 +08:00
|
|
|
err := UnmarshalJSON(data, &v)
|
|
|
|
|
return v, err
|
2026-04-30 22:16:01 +08:00
|
|
|
}
|
|
|
|
|
|
2026-04-22 01:52:30 +08:00
|
|
|
// --- Others (Keep logic but clean style) ---
|
|
|
|
|
|
2026-04-30 22:16:01 +08:00
|
|
|
func Split(s, sep string) []string {
|
2026-04-22 01:52:30 +08:00
|
|
|
ss := strings.Split(s, sep)
|
2026-04-30 22:16:01 +08:00
|
|
|
out := make([]string, 0, len(ss))
|
|
|
|
|
for _, s1 := range ss {
|
|
|
|
|
if s2 := strings.TrimSpace(s1); s2 != "" {
|
|
|
|
|
out = append(out, s2)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return out
|
2026-04-22 01:52:30 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func SplitArgs(s string) []string {
|
2026-04-30 22:16:01 +08:00
|
|
|
var res []string
|
|
|
|
|
var builder strings.Builder
|
2026-04-22 01:52:30 +08:00
|
|
|
inQuote := false
|
2026-04-30 22:16:01 +08:00
|
|
|
escaped := false
|
|
|
|
|
|
|
|
|
|
chars := []rune(s)
|
|
|
|
|
for i := 0; i < len(chars); i++ {
|
2026-04-22 01:52:30 +08:00
|
|
|
c := chars[i]
|
2026-04-30 22:16:01 +08:00
|
|
|
|
|
|
|
|
if escaped {
|
|
|
|
|
builder.WriteRune(c)
|
|
|
|
|
escaped = false
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if c == '\\' {
|
|
|
|
|
escaped = true
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if c == '"' {
|
2026-04-22 01:52:30 +08:00
|
|
|
inQuote = !inQuote
|
2026-04-30 22:16:01 +08:00
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if c == ' ' && !inQuote {
|
|
|
|
|
if builder.Len() > 0 {
|
|
|
|
|
res = append(res, builder.String())
|
|
|
|
|
builder.Reset()
|
|
|
|
|
}
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
builder.WriteRune(c)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if builder.Len() > 0 {
|
|
|
|
|
res = append(res, builder.String())
|
|
|
|
|
}
|
|
|
|
|
return res
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func JoinArgs(arr []string, sep string) string {
|
|
|
|
|
var builder strings.Builder
|
|
|
|
|
for i, s := range arr {
|
|
|
|
|
if i > 0 {
|
|
|
|
|
builder.WriteString(sep)
|
|
|
|
|
}
|
|
|
|
|
// 如果包含空格或引号,则需要包裹引号并转义内部引号
|
|
|
|
|
if strings.ContainsRune(s, ' ') || strings.ContainsRune(s, '"') {
|
|
|
|
|
builder.WriteByte('"')
|
|
|
|
|
builder.WriteString(strings.ReplaceAll(s, "\"", "\\\""))
|
|
|
|
|
builder.WriteByte('"')
|
2026-04-22 01:52:30 +08:00
|
|
|
} else {
|
2026-04-30 22:16:01 +08:00
|
|
|
builder.WriteString(s)
|
2026-04-22 01:52:30 +08:00
|
|
|
}
|
|
|
|
|
}
|
2026-04-30 22:16:01 +08:00
|
|
|
return builder.String()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func UniqueAppend(to []string, from ...any) []string {
|
|
|
|
|
exists := make(map[string]struct{}, len(to))
|
|
|
|
|
for _, s := range to {
|
|
|
|
|
exists[s] = struct{}{}
|
|
|
|
|
}
|
|
|
|
|
for _, a := range from {
|
|
|
|
|
s := String(a)
|
|
|
|
|
if _, ok := exists[s]; !ok {
|
|
|
|
|
to = append(to, s)
|
|
|
|
|
exists[s] = struct{}{}
|
2026-04-22 01:52:30 +08:00
|
|
|
}
|
|
|
|
|
}
|
2026-04-30 22:16:01 +08:00
|
|
|
return to
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func ArrayToBoolMap[T comparable](arr []T) map[T]bool {
|
|
|
|
|
r := map[T]bool{}
|
|
|
|
|
for _, s := range arr {
|
|
|
|
|
r[s] = true
|
|
|
|
|
}
|
|
|
|
|
return r
|
2026-04-22 01:52:30 +08:00
|
|
|
}
|
|
|
|
|
|
2026-05-04 10:41:34 +08:00
|
|
|
// ToMap 泛型构建新 Map
|
2026-05-04 11:24:07 +08:00
|
|
|
func ToMap[K comparable, V any](source any) (map[K]V, error) {
|
2026-05-04 10:41:34 +08:00
|
|
|
m := make(map[K]V)
|
2026-05-04 14:10:18 +08:00
|
|
|
performRecursiveTo(reflect.ValueOf(source), reflect.ValueOf(m))
|
2026-05-04 11:24:07 +08:00
|
|
|
return m, nil
|
2026-05-04 10:41:34 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ToSlice 泛型构建新 Slice
|
2026-05-04 11:24:07 +08:00
|
|
|
func ToSlice[T any](source any) ([]T, error) {
|
2026-05-04 10:41:34 +08:00
|
|
|
var s []T
|
2026-05-04 14:10:18 +08:00
|
|
|
performRecursiveTo(reflect.ValueOf(source), reflect.ValueOf(&s).Elem())
|
2026-05-04 11:24:07 +08:00
|
|
|
return s, nil
|
2026-05-04 10:41:34 +08:00
|
|
|
}
|
|
|
|
|
|
2026-05-04 14:10:18 +08:00
|
|
|
// FillMap 将 source 填充到目标 map 中
|
2026-05-04 10:41:34 +08:00
|
|
|
func FillMap(target any, source any) {
|
2026-05-04 14:10:18 +08:00
|
|
|
Convert(target, source)
|
2026-05-04 10:41:34 +08:00
|
|
|
}
|
2026-05-04 09:22:34 +08:00
|
|
|
|
2026-05-04 14:10:18 +08:00
|
|
|
// FillSlice 将 source 填充到目标 slice 中
|
2026-05-04 10:41:34 +08:00
|
|
|
func FillSlice(target any, source any) {
|
2026-05-04 14:10:18 +08:00
|
|
|
Convert(target, source)
|
2026-05-04 09:22:34 +08:00
|
|
|
}
|
|
|
|
|
|
2026-04-22 01:52:30 +08:00
|
|
|
// 补充缺失的 Key 转换工具
|
|
|
|
|
func GetLowerName(s string) string {
|
2026-05-04 09:22:34 +08:00
|
|
|
if len(s) > 0 && s[0] >= 'A' && s[0] <= 'Z' {
|
|
|
|
|
hasLower := false
|
|
|
|
|
for i := 0; i < len(s); i++ {
|
|
|
|
|
if s[i] >= 'a' && s[i] <= 'z' {
|
|
|
|
|
hasLower = true
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if hasLower {
|
|
|
|
|
return strings.ToLower(s[:1]) + s[1:]
|
|
|
|
|
}
|
2026-04-30 22:16:01 +08:00
|
|
|
}
|
2026-05-04 09:22:34 +08:00
|
|
|
return s
|
2026-04-22 01:52:30 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func GetUpperName(s string) string {
|
2026-04-30 22:16:01 +08:00
|
|
|
if s == "" {
|
|
|
|
|
return ""
|
|
|
|
|
}
|
2026-04-22 01:52:30 +08:00
|
|
|
return strings.ToUpper(s[:1]) + s[1:]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 指针工具
|
2026-04-30 22:16:01 +08:00
|
|
|
func Ptr[T any](v T) *T { return &v }
|
2026-04-22 01:52:30 +08:00
|
|
|
|
|
|
|
|
// FixUpperCase (保留以支持历史复杂的 Key 转换需求)
|
|
|
|
|
func FixUpperCase(data []byte, excludesKeys []string) {
|
|
|
|
|
// 原有逻辑保持
|
|
|
|
|
n := len(data)
|
|
|
|
|
types, keys, tpos := make([]bool, 0), make([]string, 0), -1
|
|
|
|
|
for i := 0; i < n-1; i++ {
|
2026-04-30 22:16:01 +08:00
|
|
|
if tpos+1 >= len(types) {
|
|
|
|
|
types = append(types, false)
|
|
|
|
|
keys = append(keys, "")
|
|
|
|
|
}
|
2026-04-22 01:52:30 +08:00
|
|
|
switch data[i] {
|
2026-04-30 22:16:01 +08:00
|
|
|
case '{':
|
|
|
|
|
tpos++
|
|
|
|
|
types[tpos] = true
|
|
|
|
|
keys[tpos] = ""
|
|
|
|
|
case '}':
|
|
|
|
|
tpos--
|
|
|
|
|
case '[':
|
|
|
|
|
tpos++
|
|
|
|
|
types[tpos] = false
|
|
|
|
|
keys[tpos] = ""
|
|
|
|
|
case ']':
|
|
|
|
|
tpos--
|
2026-04-22 01:52:30 +08:00
|
|
|
case '"':
|
|
|
|
|
keyPos := -1
|
2026-04-30 22:16:01 +08:00
|
|
|
if i > 0 && (data[i-1] == '{' || (data[i-1] == ',' && tpos >= 0 && types[tpos])) {
|
|
|
|
|
keyPos = i + 1
|
|
|
|
|
}
|
2026-04-22 01:52:30 +08:00
|
|
|
i++
|
|
|
|
|
for ; i < n-1; i++ {
|
2026-04-30 22:16:01 +08:00
|
|
|
if data[i] == '\\' {
|
|
|
|
|
i++
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
if data[i] == '"' {
|
|
|
|
|
if keyPos >= 0 && len(excludesKeys) > 0 {
|
|
|
|
|
keys[tpos] = string(data[keyPos:i])
|
|
|
|
|
}
|
|
|
|
|
break
|
|
|
|
|
}
|
2026-04-22 01:52:30 +08:00
|
|
|
}
|
|
|
|
|
if keyPos >= 0 && (data[keyPos] >= 'A' && data[keyPos] <= 'Z') {
|
|
|
|
|
excluded := false
|
|
|
|
|
if len(excludesKeys) > 0 {
|
|
|
|
|
checkStr := strings.Join(keys[0:tpos+1], ".")
|
|
|
|
|
for _, ek := range excludesKeys {
|
2026-04-30 22:16:01 +08:00
|
|
|
if strings.HasSuffix(ek, ".") {
|
|
|
|
|
excluded = strings.HasPrefix(checkStr, ek)
|
|
|
|
|
} else {
|
|
|
|
|
excluded = checkStr == ek
|
|
|
|
|
}
|
|
|
|
|
if excluded {
|
|
|
|
|
break
|
|
|
|
|
}
|
2026-04-22 01:52:30 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if !excluded {
|
|
|
|
|
hasLower := false
|
2026-04-30 22:16:01 +08:00
|
|
|
for _, b := range data[keyPos:i] {
|
|
|
|
|
if b >= 'a' && b <= 'z' {
|
|
|
|
|
hasLower = true
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if hasLower {
|
|
|
|
|
data[keyPos] |= 0x20
|
2026-04-22 01:52:30 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|