feat: 增强错误处理与示例
This commit is contained in:
parent
72d670be0b
commit
7d17293df3
231
errors.go
Normal file
231
errors.go
Normal file
@ -0,0 +1,231 @@
|
|||||||
|
package reflux
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 预定义的基础错误
|
||||||
|
|
||||||
|
// ErrEmptyPath 表示路径为空
|
||||||
|
var ErrEmptyPath = errors.New("rfx: empty path")
|
||||||
|
|
||||||
|
// ErrAppendNotSupported 表示 Append 操作不支持当前类型
|
||||||
|
var ErrAppendNotSupported = errors.New("rfx: Append only supports slice type")
|
||||||
|
|
||||||
|
// ErrAppendNilValue 表示无法向 nil 值追加
|
||||||
|
var ErrAppendNilValue = errors.New("rfx: cannot append to nil value")
|
||||||
|
|
||||||
|
// ErrNilPointerInPath 表示路径中存在 nil 指针或接口
|
||||||
|
var ErrNilPointerInPath = errors.New("rfx: nil pointer/interface in path")
|
||||||
|
|
||||||
|
// ErrInvalidValueInPath 表示路径中的值无效
|
||||||
|
var ErrInvalidValueInPath = errors.New("rfx: invalid value in path")
|
||||||
|
|
||||||
|
// ErrValueCannotBeSet 表示值无法被设置
|
||||||
|
var ErrValueCannotBeSet = errors.New("rfx: value cannot be set")
|
||||||
|
|
||||||
|
// ErrTargetNilPointer 表示目标是 nil 指针或接口
|
||||||
|
var ErrTargetNilPointer = errors.New("rfx: target is nil pointer/interface")
|
||||||
|
|
||||||
|
// ErrInvalidValue 表示值无效的基础错误
|
||||||
|
var ErrInvalidValue = errors.New("rfx: invalid value")
|
||||||
|
|
||||||
|
// ErrCannotSet 表示无法设置的基础错误
|
||||||
|
var ErrCannotSet = errors.New("rfx: cannot set value")
|
||||||
|
|
||||||
|
// ErrFieldNotFound 表示字段不存在的基础错误
|
||||||
|
var ErrFieldNotFound = errors.New("rfx: field not found")
|
||||||
|
|
||||||
|
// ErrKeyNotFound 表示键不存在的基础错误
|
||||||
|
var ErrKeyNotFound = errors.New("rfx: key not found")
|
||||||
|
|
||||||
|
// ErrValueNotFound 表示值不存在的基础错误
|
||||||
|
var ErrValueNotFound = errors.New("rfx: value not found")
|
||||||
|
|
||||||
|
// ErrInvalidIndex 表示索引无效的基础错误
|
||||||
|
var ErrInvalidIndex = errors.New("rfx: invalid index")
|
||||||
|
|
||||||
|
// ErrElementInvalid 表示元素无效的基础错误
|
||||||
|
var ErrElementInvalid = errors.New("rfx: element invalid")
|
||||||
|
|
||||||
|
// ErrUnsupportedType 表示不支持的类型的基础错误
|
||||||
|
var ErrUnsupportedType = errors.New("rfx: unsupported type")
|
||||||
|
|
||||||
|
// ErrCannotAssign 表示无法赋值的基础错误
|
||||||
|
var ErrCannotAssign = errors.New("rfx: cannot assign value")
|
||||||
|
|
||||||
|
// ErrIndexOutOfRange 表示索引越界的基础错误
|
||||||
|
var ErrIndexOutOfRange = errors.New("rfx: index out of range")
|
||||||
|
|
||||||
|
// ErrNormalizeFailed 表示规范化失败的基础错误
|
||||||
|
var ErrNormalizeFailed = errors.New("rfx: normalize failed")
|
||||||
|
|
||||||
|
// ErrCannotConvert 表示无法转换的基础错误
|
||||||
|
var ErrCannotConvert = errors.New("rfx: cannot convert")
|
||||||
|
|
||||||
|
// ErrSetFailed 表示设置失败的基础错误
|
||||||
|
var ErrSetFailed = errors.New("rfx: set failed")
|
||||||
|
|
||||||
|
// ErrInvalidValue 相关
|
||||||
|
|
||||||
|
// NewErrInvalidValueForPath 创建路径值无效错误
|
||||||
|
func NewErrInvalidValueForPath(path string) error {
|
||||||
|
return fmt.Errorf("%w for path '%s'", ErrInvalidValue, path)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrCannotSet 相关
|
||||||
|
|
||||||
|
// NewErrCannotSetPath 创建无法设置路径错误
|
||||||
|
func NewErrCannotSetPath(path string) error {
|
||||||
|
return fmt.Errorf("%w at path '%s'", ErrCannotSet, path)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewErrFieldCannotSet 创建字段无法设置错误
|
||||||
|
func NewErrFieldCannotSet(fieldName string, fieldType reflect.Type) error {
|
||||||
|
return fmt.Errorf("%w, field '%s' (type: %s) (unexported or not addressable)", ErrCannotSet, fieldName, fieldType)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewErrSliceElementCannotSet 创建切片元素无法设置错误
|
||||||
|
func NewErrSliceElementCannotSet(index int) error {
|
||||||
|
return fmt.Errorf("%w, slice element at index %d", ErrCannotSet, index)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewErrArrayElementCannotSet 创建数组元素无法设置错误
|
||||||
|
func NewErrArrayElementCannotSet(index int) error {
|
||||||
|
return fmt.Errorf("%w, array element at index %d", ErrCannotSet, index)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrSetFailed 相关
|
||||||
|
|
||||||
|
// NewErrSetFailed 创建设置失败错误(包装底层错误)
|
||||||
|
func NewErrSetFailed(path string, err error) error {
|
||||||
|
return fmt.Errorf("%w at path '%s': %w", ErrSetFailed, path, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewErrSetSliceElementFailed 创建设置切片元素失败错误
|
||||||
|
func NewErrSetSliceElementFailed(index int, err error) error {
|
||||||
|
return fmt.Errorf("%w, slice element at index %d: %w", ErrSetFailed, index, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewErrSetStructFieldFailed 创建设置结构体字段失败错误
|
||||||
|
func NewErrSetStructFieldFailed(fieldName string, err error) error {
|
||||||
|
return fmt.Errorf("%w, struct field '%s': %w", ErrSetFailed, fieldName, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append 相关
|
||||||
|
|
||||||
|
// NewErrAppendItemFailed 创建追加元素失败错误
|
||||||
|
func NewErrAppendItemFailed(index int, err error) error {
|
||||||
|
return fmt.Errorf("rfx: failed to append item at index %d to slice: %w", index, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrFieldNotFound / ErrKeyNotFound / ErrValueNotFound 相关
|
||||||
|
|
||||||
|
// NewErrFieldNotFound 创建字段不存在错误
|
||||||
|
func NewErrFieldNotFound(fieldName string) error {
|
||||||
|
return fmt.Errorf("%w, '%s' in struct", ErrFieldNotFound, fieldName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewErrFieldNotFoundInStruct 创建结构体中字段不存在错误(包含类型信息)
|
||||||
|
func NewErrFieldNotFoundInStruct(fieldName string, structType reflect.Type) error {
|
||||||
|
return fmt.Errorf("%w, '%s' in struct %s", ErrFieldNotFound, fieldName, structType)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewErrKeyNotFound 创建键不存在错误
|
||||||
|
func NewErrKeyNotFound(key string) error {
|
||||||
|
return fmt.Errorf("%w, '%s' in map", ErrKeyNotFound, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewErrValueNotFound 创建值不存在错误
|
||||||
|
func NewErrValueNotFound(key string) error {
|
||||||
|
return fmt.Errorf("%w for key '%s' in map", ErrValueNotFound, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrInvalidIndex / ErrIndexOutOfRange / ErrElementInvalid 相关
|
||||||
|
|
||||||
|
// NewErrInvalidIndex 创建索引无效错误
|
||||||
|
func NewErrInvalidIndex(index string, length int) error {
|
||||||
|
return fmt.Errorf("%w, '%s' for slice/array (len=%d)", ErrInvalidIndex, index, length)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewErrInvalidSliceIndex 创建切片索引无效错误
|
||||||
|
func NewErrInvalidSliceIndex(index string, err error) error {
|
||||||
|
return fmt.Errorf("%w, slice index '%s': %w", ErrInvalidIndex, index, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewErrInvalidArrayIndex 创建数组索引无效错误
|
||||||
|
func NewErrInvalidArrayIndex(index string, err error) error {
|
||||||
|
return fmt.Errorf("%w, array index '%s': %w", ErrInvalidIndex, index, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewErrSliceIndexOutOfRange 创建切片索引越界错误
|
||||||
|
func NewErrSliceIndexOutOfRange(index, length int) error {
|
||||||
|
return fmt.Errorf("%w, slice index %d (len=%d)", ErrIndexOutOfRange, index, length)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewErrArrayIndexOutOfRange 创建数组索引越界错误
|
||||||
|
func NewErrArrayIndexOutOfRange(index, length int) error {
|
||||||
|
return fmt.Errorf("%w, array index %d (len=%d)", ErrIndexOutOfRange, index, length)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewErrElementInvalid 创建元素无效错误
|
||||||
|
func NewErrElementInvalid(index string) error {
|
||||||
|
return fmt.Errorf("%w at index '%s'", ErrElementInvalid, index)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrUnsupportedType 相关
|
||||||
|
|
||||||
|
// NewErrUnsupportedType 创建不支持的类型错误
|
||||||
|
func NewErrUnsupportedType(kind reflect.Kind, key string) error {
|
||||||
|
return fmt.Errorf("%w, %s at key '%s'", ErrUnsupportedType, kind, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewErrUnsupportedTargetType 创建不支持的目标类型错误
|
||||||
|
func NewErrUnsupportedTargetType(kind reflect.Kind) error {
|
||||||
|
return fmt.Errorf("%w, %s", ErrUnsupportedType, kind)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewErrUnsupportedTargetTypeForValue 创建不支持的目标类型错误(包含值类型)
|
||||||
|
func NewErrUnsupportedTargetTypeForValue(targetType reflect.Type, inputType string) error {
|
||||||
|
return fmt.Errorf("%w, target %s for value type %s", ErrUnsupportedType, targetType, inputType)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrCannotAssign 相关
|
||||||
|
|
||||||
|
// NewErrCannotAssign 创建无法赋值错误
|
||||||
|
func NewErrCannotAssign(valueType, targetType reflect.Type) error {
|
||||||
|
return fmt.Errorf("%w, type %s to map value type %s", ErrCannotAssign, valueType, targetType)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrNormalizeFailed 相关
|
||||||
|
|
||||||
|
// NewErrNormalizeInputFailed 创建输入规范化失败错误
|
||||||
|
func NewErrNormalizeInputFailed(err error) error {
|
||||||
|
return fmt.Errorf("%w, input value: %w", ErrNormalizeFailed, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrCannotConvert 相关
|
||||||
|
|
||||||
|
// NewErrCannotConvertToSlice 创建无法转换为切片错误
|
||||||
|
func NewErrCannotConvertToSlice(sourceType, targetType reflect.Type) error {
|
||||||
|
return fmt.Errorf("%w, %s to slice type %s", ErrCannotConvert, sourceType, targetType)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewErrCannotConvertToStruct 创建无法转换为结构体错误
|
||||||
|
func NewErrCannotConvertToStruct(sourceType, targetType reflect.Type) error {
|
||||||
|
return fmt.Errorf("%w, %s to struct type %s", ErrCannotConvert, sourceType, targetType)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewErrCannotConvertValue 创建无法转换值错误
|
||||||
|
func NewErrCannotConvertValue(inputType string, targetType reflect.Type, err error) error {
|
||||||
|
return fmt.Errorf("%w, value of type %s to target type %s: %w", ErrCannotConvert, inputType, targetType, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 其他
|
||||||
|
|
||||||
|
// NewErrUnmarshalFailed 创建反序列化失败错误
|
||||||
|
func NewErrUnmarshalFailed(targetType reflect.Type, err error) error {
|
||||||
|
return fmt.Errorf("rfx: failed to unmarshal JSON into type %s: %w", targetType, err)
|
||||||
|
}
|
||||||
150
errors_example_test.go
Normal file
150
errors_example_test.go
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
package reflux_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"git.fsdpf.net/go/reflux"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ExampleSet_errorMessages 演示设置失败时的详细错误信息
|
||||||
|
func ExampleSet_errorMessages() {
|
||||||
|
type Person struct {
|
||||||
|
Name string
|
||||||
|
Age int
|
||||||
|
}
|
||||||
|
|
||||||
|
p := &Person{Name: "Alice", Age: 30}
|
||||||
|
r := reflux.New(p)
|
||||||
|
|
||||||
|
// 示例1: 类型不匹配错误 - 显示原有类型和传入值的类型
|
||||||
|
defer func() {
|
||||||
|
if rec := recover(); rec != nil {
|
||||||
|
fmt.Println("错误:", rec)
|
||||||
|
// 输出类似: rfx: set failed at path 'Age': cannot convert value of type map[string]int to target type int: ...
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// 尝试将 map 设置为 int 类型字段,会显示详细的类型转换错误
|
||||||
|
r.Set("Age", map[string]int{"invalid": 123})
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExampleSet_fieldNotFound 演示字段不存在的错误信息
|
||||||
|
func ExampleSet_fieldNotFound() {
|
||||||
|
type Person struct {
|
||||||
|
Name string
|
||||||
|
Age int
|
||||||
|
}
|
||||||
|
|
||||||
|
p := &Person{Name: "Bob", Age: 25}
|
||||||
|
r := reflux.New(p)
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
if rec := recover(); rec != nil {
|
||||||
|
fmt.Println(rec)
|
||||||
|
// Output: rfx: set failed at path 'InvalidField': rfx: field not found, 'InvalidField' in struct reflux_test.Person
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
r.Set("InvalidField", "value")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExampleSet_sliceIndexOutOfRange 演示切片索引越界的错误信息
|
||||||
|
func ExampleSet_sliceIndexOutOfRange() {
|
||||||
|
type Data struct {
|
||||||
|
Items []int
|
||||||
|
}
|
||||||
|
|
||||||
|
d := &Data{Items: []int{1, 2, 3}}
|
||||||
|
r := reflux.New(d)
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
if rec := recover(); rec != nil {
|
||||||
|
fmt.Println(rec)
|
||||||
|
// Output: rfx: set failed at path 'Items.10': rfx: index out of range, slice index 10 (len=3)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
r.Set("Items.10", 999)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExampleSet_unexportedField 演示未导出字段的错误信息
|
||||||
|
func ExampleSet_unexportedField() {
|
||||||
|
type Person struct {
|
||||||
|
Name string
|
||||||
|
age int // 未导出字段
|
||||||
|
}
|
||||||
|
|
||||||
|
p := &Person{Name: "Charlie"}
|
||||||
|
r := reflux.New(p)
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
if rec := recover(); rec != nil {
|
||||||
|
fmt.Println(rec)
|
||||||
|
// Output: rfx: set failed at path 'age': rfx: cannot set value, field 'age' (type: int) (unexported or not addressable)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
r.Set("age", 40)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExampleAppend_typeConversionError 演示 Append 时的类型转换错误
|
||||||
|
func ExampleAppend_typeConversionError() {
|
||||||
|
type Data struct {
|
||||||
|
Items []int
|
||||||
|
}
|
||||||
|
|
||||||
|
d := &Data{Items: []int{1, 2, 3}}
|
||||||
|
r := reflux.New(d).Get("Items")
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
if rec := recover(); rec != nil {
|
||||||
|
fmt.Println(rec)
|
||||||
|
// Output: rfx: failed to append item at index 0 to slice: cannot convert value of type map[string]int to target type int: ...
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// 尝试追加无法转换为 int 的 map
|
||||||
|
r.Append(map[string]int{"invalid": 123})
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExampleSet_nestedPathError 演示嵌套路径中的错误信息
|
||||||
|
func ExampleSet_nestedPathError() {
|
||||||
|
type Address struct {
|
||||||
|
City string
|
||||||
|
}
|
||||||
|
type Person struct {
|
||||||
|
Name string
|
||||||
|
Address Address
|
||||||
|
}
|
||||||
|
|
||||||
|
p := &Person{
|
||||||
|
Name: "David",
|
||||||
|
Address: Address{City: "NYC"},
|
||||||
|
}
|
||||||
|
r := reflux.New(p)
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
if rec := recover(); rec != nil {
|
||||||
|
fmt.Println(rec)
|
||||||
|
// Output: rfx: set failed at path 'Address.Country': rfx: field not found, 'Country' in struct reflux_test.Address
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
r.Set("Address.Country", "USA")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExampleSet_successfulTypeConversion 演示成功的类型转换
|
||||||
|
func ExampleSet_successfulTypeConversion() {
|
||||||
|
type Person struct {
|
||||||
|
Name string
|
||||||
|
Age int
|
||||||
|
}
|
||||||
|
|
||||||
|
p := &Person{Name: "Eve", Age: 20}
|
||||||
|
r := reflux.New(p)
|
||||||
|
|
||||||
|
// 字符串 "30" 可以成功转换为 int
|
||||||
|
r.Set("Age", "30")
|
||||||
|
fmt.Printf("Name: %s, Age: %d\n", p.Name, p.Age)
|
||||||
|
// Output: Name: Eve, Age: 30
|
||||||
|
}
|
||||||
144
rfx.go
144
rfx.go
@ -37,32 +37,32 @@ func (r *rfx) Scope(p ...string) R {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set 设置指定路径的值,支持链式调用
|
// Set 设置指定路径的值,支持链式调用
|
||||||
// 如果路径不存在或设置失败,会 panic
|
// 如果路径不存在或设置失败,会 panic 并提供详细错误信息
|
||||||
func (r *rfx) Set(key string, v any) R {
|
func (r *rfx) Set(key string, v any) R {
|
||||||
// 展开路径
|
// 展开路径
|
||||||
keys := expandPath(key)
|
keys := expandPath(key)
|
||||||
if len(keys) == 0 {
|
if len(keys) == 0 {
|
||||||
panic("rfx: empty path")
|
panic(ErrEmptyPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果只有一个键,直接设置
|
// 如果只有一个键,直接设置
|
||||||
if len(keys) == 1 {
|
if len(keys) == 1 {
|
||||||
target := r.getParentValue()
|
target := r.getParentValue()
|
||||||
if !target.IsValid() {
|
if !target.IsValid() {
|
||||||
panic(fmt.Sprintf("rfx: invalid value for path '%s'", key))
|
panic(NewErrInvalidValueForPath(key))
|
||||||
}
|
}
|
||||||
if !target.CanSet() {
|
if !target.CanSet() {
|
||||||
panic(fmt.Sprintf("rfx: cannot set value at path '%s'", key))
|
panic(NewErrCannotSetPath(key))
|
||||||
}
|
}
|
||||||
if !r.setFieldValue(target, keys[0], v) {
|
if err := r.setFieldValue(target, keys[0], v); err != nil {
|
||||||
panic(fmt.Sprintf("rfx: failed to set value at path '%s'", key))
|
panic(NewErrSetFailed(key, err))
|
||||||
}
|
}
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
// 多个键的情况,需要特殊处理 map 中的 struct
|
// 多个键的情况,需要特殊处理 map 中的 struct
|
||||||
if !r.setNestedValue(r.value, keys, v) {
|
if err := r.setNestedValue(r.value, keys, v); err != nil {
|
||||||
panic(fmt.Sprintf("rfx: failed to set value at path '%s'", key))
|
panic(NewErrSetFailed(key, err))
|
||||||
}
|
}
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
@ -79,13 +79,13 @@ func (r *rfx) Append(items ...any) R {
|
|||||||
target := r.getParentValue()
|
target := r.getParentValue()
|
||||||
for target.Kind() == reflect.Ptr || target.Kind() == reflect.Interface {
|
for target.Kind() == reflect.Ptr || target.Kind() == reflect.Interface {
|
||||||
if target.IsNil() {
|
if target.IsNil() {
|
||||||
panic("rfx: cannot append to nil value")
|
panic(ErrAppendNilValue)
|
||||||
}
|
}
|
||||||
target = target.Elem()
|
target = target.Elem()
|
||||||
}
|
}
|
||||||
|
|
||||||
if !target.IsValid() || target.Kind() != reflect.Slice {
|
if !target.IsValid() || target.Kind() != reflect.Slice {
|
||||||
panic("rfx: Append only supports slice type")
|
panic(ErrAppendNotSupported)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 一次性构造所有要追加的元素,然后调用一次 reflect.Append
|
// 一次性构造所有要追加的元素,然后调用一次 reflect.Append
|
||||||
@ -93,8 +93,8 @@ func (r *rfx) Append(items ...any) R {
|
|||||||
newValues := make([]reflect.Value, len(items))
|
newValues := make([]reflect.Value, len(items))
|
||||||
for i, item := range items {
|
for i, item := range items {
|
||||||
newElem := reflect.New(elemType).Elem()
|
newElem := reflect.New(elemType).Elem()
|
||||||
if !r.setValue(newElem, item) {
|
if err := r.setValue(newElem, item); err != nil {
|
||||||
panic("rfx: failed to append item to slice")
|
panic(NewErrAppendItemFailed(i, err))
|
||||||
}
|
}
|
||||||
newValues[i] = newElem
|
newValues[i] = newElem
|
||||||
}
|
}
|
||||||
@ -104,23 +104,23 @@ func (r *rfx) Append(items ...any) R {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// setNestedValue 递归设置嵌套值,特殊处理 map 中的 struct
|
// setNestedValue 递归设置嵌套值,特殊处理 map 中的 struct
|
||||||
func (r *rfx) setNestedValue(current reflect.Value, keys []string, v any) bool {
|
func (r *rfx) setNestedValue(current reflect.Value, keys []string, v any) error {
|
||||||
// 解引用指针和接口
|
// 解引用指针和接口
|
||||||
for current.Kind() == reflect.Ptr || current.Kind() == reflect.Interface {
|
for current.Kind() == reflect.Ptr || current.Kind() == reflect.Interface {
|
||||||
if current.IsNil() {
|
if current.IsNil() {
|
||||||
return false
|
return ErrNilPointerInPath
|
||||||
}
|
}
|
||||||
current = current.Elem()
|
current = current.Elem()
|
||||||
}
|
}
|
||||||
|
|
||||||
if !current.IsValid() {
|
if !current.IsValid() {
|
||||||
return false
|
return ErrInvalidValueInPath
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果只剩一个键,直接设置
|
// 如果只剩一个键,直接设置
|
||||||
if len(keys) == 1 {
|
if len(keys) == 1 {
|
||||||
if !current.CanSet() {
|
if !current.CanSet() {
|
||||||
return false
|
return ErrValueCannotBeSet
|
||||||
}
|
}
|
||||||
return r.setFieldValue(current, keys[0], v)
|
return r.setFieldValue(current, keys[0], v)
|
||||||
}
|
}
|
||||||
@ -133,7 +133,7 @@ func (r *rfx) setNestedValue(current reflect.Value, keys []string, v any) bool {
|
|||||||
case reflect.Struct:
|
case reflect.Struct:
|
||||||
field := tryStructFieldValue(current, firstKey)
|
field := tryStructFieldValue(current, firstKey)
|
||||||
if !field.IsValid() {
|
if !field.IsValid() {
|
||||||
return false
|
return NewErrFieldNotFound(firstKey)
|
||||||
}
|
}
|
||||||
return r.setNestedValue(field, remainingKeys, v)
|
return r.setNestedValue(field, remainingKeys, v)
|
||||||
|
|
||||||
@ -142,12 +142,12 @@ func (r *rfx) setNestedValue(current reflect.Value, keys []string, v any) bool {
|
|||||||
// 使用 tryMapFieldKey 获取实际的键
|
// 使用 tryMapFieldKey 获取实际的键
|
||||||
actualKey := tryMapFieldKey(current, firstKey)
|
actualKey := tryMapFieldKey(current, firstKey)
|
||||||
if !actualKey.IsValid() {
|
if !actualKey.IsValid() {
|
||||||
return false
|
return NewErrKeyNotFound(firstKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
mapValue := current.MapIndex(actualKey)
|
mapValue := current.MapIndex(actualKey)
|
||||||
if !mapValue.IsValid() {
|
if !mapValue.IsValid() {
|
||||||
return false
|
return NewErrValueNotFound(firstKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 解开 interface 包装获取实际的值
|
// 解开 interface 包装获取实际的值
|
||||||
@ -161,27 +161,27 @@ func (r *rfx) setNestedValue(current reflect.Value, keys []string, v any) bool {
|
|||||||
valueCopy.Set(actualValue)
|
valueCopy.Set(actualValue)
|
||||||
|
|
||||||
// 在副本上递归设置值
|
// 在副本上递归设置值
|
||||||
if !r.setNestedValue(valueCopy, remainingKeys, v) {
|
if err := r.setNestedValue(valueCopy, remainingKeys, v); err != nil {
|
||||||
return false
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// 将修改后的值设置回 map,使用实际找到的键
|
// 将修改后的值设置回 map,使用实际找到的键
|
||||||
current.SetMapIndex(actualKey, valueCopy)
|
current.SetMapIndex(actualKey, valueCopy)
|
||||||
return true
|
return nil
|
||||||
|
|
||||||
case reflect.Slice, reflect.Array:
|
case reflect.Slice, reflect.Array:
|
||||||
idx, err := strconv.Atoi(firstKey)
|
idx, err := strconv.Atoi(firstKey)
|
||||||
if err != nil || idx < 0 || idx >= current.Len() {
|
if err != nil || idx < 0 || idx >= current.Len() {
|
||||||
return false
|
return NewErrInvalidIndex(firstKey, current.Len())
|
||||||
}
|
}
|
||||||
elem := current.Index(idx)
|
elem := current.Index(idx)
|
||||||
if !elem.IsValid() {
|
if !elem.IsValid() {
|
||||||
return false
|
return NewErrElementInvalid(firstKey)
|
||||||
}
|
}
|
||||||
return r.setNestedValue(elem, remainingKeys, v)
|
return r.setNestedValue(elem, remainingKeys, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return NewErrUnsupportedType(current.Kind(), firstKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
// getParentValue 获取父级值的辅助方法
|
// getParentValue 获取父级值的辅助方法
|
||||||
@ -200,10 +200,11 @@ func (r *rfx) getParentValue(p ...string) reflect.Value {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// setFieldValue 设置字段值的辅助方法
|
// setFieldValue 设置字段值的辅助方法
|
||||||
func (r *rfx) setFieldValue(target reflect.Value, key string, v any) bool {
|
// 返回 error 包含详细的设置失败信息
|
||||||
|
func (r *rfx) setFieldValue(target reflect.Value, key string, v any) error {
|
||||||
for target.Kind() == reflect.Ptr || target.Kind() == reflect.Interface {
|
for target.Kind() == reflect.Ptr || target.Kind() == reflect.Interface {
|
||||||
if target.IsNil() {
|
if target.IsNil() {
|
||||||
return false
|
return ErrTargetNilPointer
|
||||||
}
|
}
|
||||||
target = target.Elem()
|
target = target.Elem()
|
||||||
}
|
}
|
||||||
@ -211,8 +212,11 @@ func (r *rfx) setFieldValue(target reflect.Value, key string, v any) bool {
|
|||||||
switch target.Kind() {
|
switch target.Kind() {
|
||||||
case reflect.Struct:
|
case reflect.Struct:
|
||||||
field := tryStructFieldValue(target, key)
|
field := tryStructFieldValue(target, key)
|
||||||
if !field.IsValid() || !field.CanSet() {
|
if !field.IsValid() {
|
||||||
return false
|
return NewErrFieldNotFoundInStruct(key, target.Type())
|
||||||
|
}
|
||||||
|
if !field.CanSet() {
|
||||||
|
return NewErrFieldCannotSet(key, field.Type())
|
||||||
}
|
}
|
||||||
return r.setValue(field, v)
|
return r.setValue(field, v)
|
||||||
case reflect.Map:
|
case reflect.Map:
|
||||||
@ -225,18 +229,18 @@ func (r *rfx) setFieldValue(target reflect.Value, key string, v any) bool {
|
|||||||
if actualKey.IsValid() {
|
if actualKey.IsValid() {
|
||||||
// 字段已存在,创建新值用于设置
|
// 字段已存在,创建新值用于设置
|
||||||
newValue := reflect.New(target.Type().Elem()).Elem()
|
newValue := reflect.New(target.Type().Elem()).Elem()
|
||||||
if !r.setValue(newValue, v) {
|
if err := r.setValue(newValue, v); err != nil {
|
||||||
return false
|
return err
|
||||||
}
|
}
|
||||||
target.SetMapIndex(actualKey, newValue)
|
target.SetMapIndex(actualKey, newValue)
|
||||||
return true
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 字段不存在,创建新的 map 值
|
// 字段不存在,创建新的 map 值
|
||||||
// 处理 nil 值的情况
|
// 处理 nil 值的情况
|
||||||
if v == nil {
|
if v == nil {
|
||||||
target.SetMapIndex(reflect.ValueOf(key), reflect.Zero(target.Type().Elem()))
|
target.SetMapIndex(reflect.ValueOf(key), reflect.Zero(target.Type().Elem()))
|
||||||
return true
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
val := reflect.ValueOf(v)
|
val := reflect.ValueOf(v)
|
||||||
@ -245,61 +249,65 @@ func (r *rfx) setFieldValue(target reflect.Value, key string, v any) bool {
|
|||||||
if val.Type().ConvertibleTo(target.Type().Elem()) {
|
if val.Type().ConvertibleTo(target.Type().Elem()) {
|
||||||
val = val.Convert(target.Type().Elem())
|
val = val.Convert(target.Type().Elem())
|
||||||
} else {
|
} else {
|
||||||
return false
|
return NewErrCannotAssign(val.Type(), target.Type().Elem())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
target.SetMapIndex(reflect.ValueOf(key), val)
|
target.SetMapIndex(reflect.ValueOf(key), val)
|
||||||
return true
|
return nil
|
||||||
case reflect.Slice:
|
case reflect.Slice:
|
||||||
idx, err := strconv.Atoi(key)
|
idx, err := strconv.Atoi(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return NewErrInvalidSliceIndex(key, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 对于切片,支持使用索引 -1 追加新元素(插入到切片前面)
|
// 对于切片,支持使用索引 -1 追加新元素(插入到切片前面)
|
||||||
if idx == -1 {
|
if idx == -1 {
|
||||||
elemType := target.Type().Elem()
|
elemType := target.Type().Elem()
|
||||||
newElem := reflect.New(elemType).Elem()
|
newElem := reflect.New(elemType).Elem()
|
||||||
if !r.setValue(newElem, v) {
|
if err := r.setValue(newElem, v); err != nil {
|
||||||
return false
|
return err
|
||||||
}
|
}
|
||||||
// 将新元素放在前面,原有元素顺序后移
|
// 将新元素放在前面,原有元素顺序后移
|
||||||
newSlice := reflect.MakeSlice(target.Type(), 0, target.Len()+1)
|
newSlice := reflect.MakeSlice(target.Type(), 0, target.Len()+1)
|
||||||
newSlice = reflect.Append(newSlice, newElem)
|
newSlice = reflect.Append(newSlice, newElem)
|
||||||
newSlice = reflect.AppendSlice(newSlice, target)
|
newSlice = reflect.AppendSlice(newSlice, target)
|
||||||
target.Set(newSlice)
|
target.Set(newSlice)
|
||||||
return true
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if idx < 0 || idx >= target.Len() {
|
if idx < 0 || idx >= target.Len() {
|
||||||
return false
|
return NewErrSliceIndexOutOfRange(idx, target.Len())
|
||||||
}
|
}
|
||||||
elem := target.Index(idx)
|
elem := target.Index(idx)
|
||||||
if !elem.CanSet() {
|
if !elem.CanSet() {
|
||||||
return false
|
return NewErrSliceElementCannotSet(idx)
|
||||||
}
|
}
|
||||||
return r.setValue(elem, v)
|
return r.setValue(elem, v)
|
||||||
case reflect.Array:
|
case reflect.Array:
|
||||||
idx, err := strconv.Atoi(key)
|
idx, err := strconv.Atoi(key)
|
||||||
if err != nil || idx < 0 || idx >= target.Len() {
|
if err != nil {
|
||||||
return false
|
return NewErrInvalidArrayIndex(key, err)
|
||||||
|
}
|
||||||
|
if idx < 0 || idx >= target.Len() {
|
||||||
|
return NewErrArrayIndexOutOfRange(idx, target.Len())
|
||||||
}
|
}
|
||||||
elem := target.Index(idx)
|
elem := target.Index(idx)
|
||||||
if !elem.CanSet() {
|
if !elem.CanSet() {
|
||||||
return false
|
return NewErrArrayElementCannotSet(idx)
|
||||||
}
|
}
|
||||||
return r.setValue(elem, v)
|
return r.setValue(elem, v)
|
||||||
}
|
}
|
||||||
return false
|
return NewErrUnsupportedTargetType(target.Kind())
|
||||||
}
|
}
|
||||||
|
|
||||||
// setValue 设置值的辅助方法
|
// setValue 设置值的辅助方法
|
||||||
// 使用 cast 库进行智能类型转换,支持更多的转换场景
|
// 使用 cast 库进行智能类型转换,支持更多的转换场景
|
||||||
func (r *rfx) setValue(field reflect.Value, v any) bool {
|
// 返回 error 包含详细的类型转换失败信息
|
||||||
|
func (r *rfx) setValue(field reflect.Value, v any) error {
|
||||||
val, _, err := normalizeInputValue(v)
|
val, _, err := normalizeInputValue(v)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return NewErrNormalizeInputFailed(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
targetType := field.Type()
|
targetType := field.Type()
|
||||||
@ -317,7 +325,7 @@ func (r *rfx) setValue(field reflect.Value, v any) bool {
|
|||||||
// 尝试直接赋值(类型完全匹配)
|
// 尝试直接赋值(类型完全匹配)
|
||||||
if val.Type().AssignableTo(targetType) {
|
if val.Type().AssignableTo(targetType) {
|
||||||
field.Set(val)
|
field.Set(val)
|
||||||
return true
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果源值是指针但目标不是指针,尝试解引用后再赋值
|
// 如果源值是指针但目标不是指针,尝试解引用后再赋值
|
||||||
@ -325,7 +333,7 @@ func (r *rfx) setValue(field reflect.Value, v any) bool {
|
|||||||
derefVal := val.Elem()
|
derefVal := val.Elem()
|
||||||
if derefVal.Type().AssignableTo(targetType) {
|
if derefVal.Type().AssignableTo(targetType) {
|
||||||
field.Set(derefVal)
|
field.Set(derefVal)
|
||||||
return true
|
return nil
|
||||||
}
|
}
|
||||||
// 解引用后继续使用下面的逻辑处理
|
// 解引用后继续使用下面的逻辑处理
|
||||||
val = derefVal
|
val = derefVal
|
||||||
@ -337,7 +345,7 @@ func (r *rfx) setValue(field reflect.Value, v any) bool {
|
|||||||
// 如果传入的值已经是指针类型,尝试直接赋值
|
// 如果传入的值已经是指针类型,尝试直接赋值
|
||||||
if val.Type().AssignableTo(targetType) {
|
if val.Type().AssignableTo(targetType) {
|
||||||
field.Set(val)
|
field.Set(val)
|
||||||
return true
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果传入的值不是指针,创建新指针并设置值
|
// 如果传入的值不是指针,创建新指针并设置值
|
||||||
@ -345,35 +353,35 @@ func (r *rfx) setValue(field reflect.Value, v any) bool {
|
|||||||
newPtr := reflect.New(elemType)
|
newPtr := reflect.New(elemType)
|
||||||
|
|
||||||
// 递归设置指针指向的值
|
// 递归设置指针指向的值
|
||||||
if !r.setValue(newPtr.Elem(), v) {
|
if err := r.setValue(newPtr.Elem(), v); err != nil {
|
||||||
return false
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
field.Set(newPtr)
|
field.Set(newPtr)
|
||||||
return true
|
return nil
|
||||||
|
|
||||||
case reflect.Slice: // 处理切片类型,支持从通用切片(如 []any)转换
|
case reflect.Slice: // 处理切片类型,支持从通用切片(如 []any)转换
|
||||||
if val.Kind() != reflect.Slice && val.Kind() != reflect.Array {
|
if val.Kind() != reflect.Slice && val.Kind() != reflect.Array {
|
||||||
return false
|
return NewErrCannotConvertToSlice(val.Type(), targetType)
|
||||||
}
|
}
|
||||||
|
|
||||||
newSlice := reflect.MakeSlice(targetType, val.Len(), val.Len())
|
newSlice := reflect.MakeSlice(targetType, val.Len(), val.Len())
|
||||||
|
|
||||||
for i := 0; i < val.Len(); i++ {
|
for i := 0; i < val.Len(); i++ {
|
||||||
if !r.setValue(newSlice.Index(i), val.Index(i)) {
|
if err := r.setValue(newSlice.Index(i), val.Index(i).Interface()); err != nil {
|
||||||
return false
|
return NewErrSetSliceElementFailed(i, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
field.Set(newSlice)
|
field.Set(newSlice)
|
||||||
|
|
||||||
return true
|
return nil
|
||||||
|
|
||||||
case reflect.Struct:
|
case reflect.Struct:
|
||||||
// 遍历目标结构体的字段,从源值(结构体或 map)中按字段名取值并设置
|
// 遍历目标结构体的字段,从源值(结构体或 map)中按字段名取值并设置
|
||||||
// 仅支持从 struct 或 map 填充
|
// 仅支持从 struct 或 map 填充
|
||||||
if val.Kind() != reflect.Struct && val.Kind() != reflect.Map {
|
if val.Kind() != reflect.Struct && val.Kind() != reflect.Map {
|
||||||
return false
|
return NewErrCannotConvertToStruct(val.Type(), targetType)
|
||||||
}
|
}
|
||||||
|
|
||||||
fieldType := field.Type()
|
fieldType := field.Type()
|
||||||
@ -390,11 +398,11 @@ func (r *rfx) setValue(field reflect.Value, v any) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 使用 setValue 复用现有的类型转换逻辑,忽略单个字段失败
|
// 使用 setValue 复用现有的类型转换逻辑,忽略单个字段失败
|
||||||
if !r.setValue(dstField, valField) {
|
if err := r.setValue(dstField, valField.Interface()); err != nil {
|
||||||
return false
|
return NewErrSetStructFieldFailed(fieldType.Field(i).Name, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true
|
return nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -432,28 +440,28 @@ func (r *rfx) setValue(field reflect.Value, v any) bool {
|
|||||||
case reflect.String:
|
case reflect.String:
|
||||||
converted, err = cast.ToStringE(v)
|
converted, err = cast.ToStringE(v)
|
||||||
default:
|
default:
|
||||||
return false
|
return NewErrUnsupportedTargetTypeForValue(targetType, fmt.Sprintf("%T", v))
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// 如果 cast 失败,尝试标准的反射类型转换作为后备
|
// 如果 cast 失败,尝试标准的反射类型转换作为后备
|
||||||
if val.IsValid() && val.Type().ConvertibleTo(field.Type()) {
|
if val.IsValid() && val.Type().ConvertibleTo(field.Type()) {
|
||||||
field.Set(val.Convert(field.Type()))
|
field.Set(val.Convert(field.Type()))
|
||||||
return true
|
return nil
|
||||||
}
|
}
|
||||||
return false
|
return NewErrCannotConvertValue(fmt.Sprintf("%T", v), targetType, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
field.Set(reflect.ValueOf(converted))
|
field.Set(reflect.ValueOf(converted))
|
||||||
|
|
||||||
return true
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete 删除指定路径的值,支持链式调用
|
// Delete 删除指定路径的值,支持链式调用
|
||||||
// 如果删除失败会 panic
|
// 如果删除失败会 panic
|
||||||
func (r *rfx) Delete(p ...string) R {
|
func (r *rfx) Delete(p ...string) R {
|
||||||
if len(p) == 0 {
|
if len(p) == 0 {
|
||||||
panic("rfx: empty path")
|
panic(ErrEmptyPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 展开路径
|
// 展开路径
|
||||||
@ -970,8 +978,8 @@ func (r *rfx) UnmarshalJSON(data []byte) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 如果类型不匹配,尝试使用 setValue 进行转换
|
// 如果类型不匹配,尝试使用 setValue 进行转换
|
||||||
if !r.setValue(target, v) {
|
if err := r.setValue(target, v); err != nil {
|
||||||
return fmt.Errorf("rfx: failed to unmarshal JSON into type %s", target.Type())
|
return NewErrUnmarshalFailed(target.Type(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
2
util.go
2
util.go
@ -360,7 +360,7 @@ func normalizeInputValue(v any) (reflect.Value, bool, error) {
|
|||||||
|
|
||||||
// 如果所有元素都是 nil 或无效,退回到通用处理
|
// 如果所有元素都是 nil 或无效,退回到通用处理
|
||||||
if elemType == nil {
|
if elemType == nil {
|
||||||
rv = reflect.ValueOf(v)
|
rv = reflect.ValueOf(nil)
|
||||||
} else {
|
} else {
|
||||||
isPtr = true
|
isPtr = true
|
||||||
count := len(vv)
|
count := len(vv)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user