diff --git a/errors.go b/errors.go new file mode 100644 index 0000000..aed6c90 --- /dev/null +++ b/errors.go @@ -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) +} diff --git a/errors_example_test.go b/errors_example_test.go new file mode 100644 index 0000000..328cd9d --- /dev/null +++ b/errors_example_test.go @@ -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 +} diff --git a/rfx.go b/rfx.go index 186edbd..90cc6c0 100644 --- a/rfx.go +++ b/rfx.go @@ -37,32 +37,32 @@ func (r *rfx) Scope(p ...string) R { } // Set 设置指定路径的值,支持链式调用 -// 如果路径不存在或设置失败,会 panic +// 如果路径不存在或设置失败,会 panic 并提供详细错误信息 func (r *rfx) Set(key string, v any) R { // 展开路径 keys := expandPath(key) if len(keys) == 0 { - panic("rfx: empty path") + panic(ErrEmptyPath) } // 如果只有一个键,直接设置 if len(keys) == 1 { target := r.getParentValue() if !target.IsValid() { - panic(fmt.Sprintf("rfx: invalid value for path '%s'", key)) + panic(NewErrInvalidValueForPath(key)) } if !target.CanSet() { - panic(fmt.Sprintf("rfx: cannot set value at path '%s'", key)) + panic(NewErrCannotSetPath(key)) } - if !r.setFieldValue(target, keys[0], v) { - panic(fmt.Sprintf("rfx: failed to set value at path '%s'", key)) + if err := r.setFieldValue(target, keys[0], v); err != nil { + panic(NewErrSetFailed(key, err)) } return r } // 多个键的情况,需要特殊处理 map 中的 struct - if !r.setNestedValue(r.value, keys, v) { - panic(fmt.Sprintf("rfx: failed to set value at path '%s'", key)) + if err := r.setNestedValue(r.value, keys, v); err != nil { + panic(NewErrSetFailed(key, err)) } return r } @@ -79,13 +79,13 @@ func (r *rfx) Append(items ...any) R { target := r.getParentValue() for target.Kind() == reflect.Ptr || target.Kind() == reflect.Interface { if target.IsNil() { - panic("rfx: cannot append to nil value") + panic(ErrAppendNilValue) } target = target.Elem() } if !target.IsValid() || target.Kind() != reflect.Slice { - panic("rfx: Append only supports slice type") + panic(ErrAppendNotSupported) } // 一次性构造所有要追加的元素,然后调用一次 reflect.Append @@ -93,8 +93,8 @@ func (r *rfx) Append(items ...any) R { newValues := make([]reflect.Value, len(items)) for i, item := range items { newElem := reflect.New(elemType).Elem() - if !r.setValue(newElem, item) { - panic("rfx: failed to append item to slice") + if err := r.setValue(newElem, item); err != nil { + panic(NewErrAppendItemFailed(i, err)) } newValues[i] = newElem } @@ -104,23 +104,23 @@ func (r *rfx) Append(items ...any) R { } // 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 { if current.IsNil() { - return false + return ErrNilPointerInPath } current = current.Elem() } if !current.IsValid() { - return false + return ErrInvalidValueInPath } // 如果只剩一个键,直接设置 if len(keys) == 1 { if !current.CanSet() { - return false + return ErrValueCannotBeSet } 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: field := tryStructFieldValue(current, firstKey) if !field.IsValid() { - return false + return NewErrFieldNotFound(firstKey) } return r.setNestedValue(field, remainingKeys, v) @@ -142,12 +142,12 @@ func (r *rfx) setNestedValue(current reflect.Value, keys []string, v any) bool { // 使用 tryMapFieldKey 获取实际的键 actualKey := tryMapFieldKey(current, firstKey) if !actualKey.IsValid() { - return false + return NewErrKeyNotFound(firstKey) } mapValue := current.MapIndex(actualKey) if !mapValue.IsValid() { - return false + return NewErrValueNotFound(firstKey) } // 解开 interface 包装获取实际的值 @@ -161,27 +161,27 @@ func (r *rfx) setNestedValue(current reflect.Value, keys []string, v any) bool { valueCopy.Set(actualValue) // 在副本上递归设置值 - if !r.setNestedValue(valueCopy, remainingKeys, v) { - return false + if err := r.setNestedValue(valueCopy, remainingKeys, v); err != nil { + return err } // 将修改后的值设置回 map,使用实际找到的键 current.SetMapIndex(actualKey, valueCopy) - return true + return nil case reflect.Slice, reflect.Array: idx, err := strconv.Atoi(firstKey) if err != nil || idx < 0 || idx >= current.Len() { - return false + return NewErrInvalidIndex(firstKey, current.Len()) } elem := current.Index(idx) if !elem.IsValid() { - return false + return NewErrElementInvalid(firstKey) } return r.setNestedValue(elem, remainingKeys, v) } - return false + return NewErrUnsupportedType(current.Kind(), firstKey) } // getParentValue 获取父级值的辅助方法 @@ -200,10 +200,11 @@ func (r *rfx) getParentValue(p ...string) reflect.Value { } // 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 { if target.IsNil() { - return false + return ErrTargetNilPointer } target = target.Elem() } @@ -211,8 +212,11 @@ func (r *rfx) setFieldValue(target reflect.Value, key string, v any) bool { switch target.Kind() { case reflect.Struct: field := tryStructFieldValue(target, key) - if !field.IsValid() || !field.CanSet() { - return false + if !field.IsValid() { + return NewErrFieldNotFoundInStruct(key, target.Type()) + } + if !field.CanSet() { + return NewErrFieldCannotSet(key, field.Type()) } return r.setValue(field, v) case reflect.Map: @@ -225,18 +229,18 @@ func (r *rfx) setFieldValue(target reflect.Value, key string, v any) bool { if actualKey.IsValid() { // 字段已存在,创建新值用于设置 newValue := reflect.New(target.Type().Elem()).Elem() - if !r.setValue(newValue, v) { - return false + if err := r.setValue(newValue, v); err != nil { + return err } target.SetMapIndex(actualKey, newValue) - return true + return nil } // 字段不存在,创建新的 map 值 // 处理 nil 值的情况 if v == nil { target.SetMapIndex(reflect.ValueOf(key), reflect.Zero(target.Type().Elem())) - return true + return nil } 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()) { val = val.Convert(target.Type().Elem()) } else { - return false + return NewErrCannotAssign(val.Type(), target.Type().Elem()) } } target.SetMapIndex(reflect.ValueOf(key), val) - return true + return nil case reflect.Slice: idx, err := strconv.Atoi(key) if err != nil { - return false + return NewErrInvalidSliceIndex(key, err) } // 对于切片,支持使用索引 -1 追加新元素(插入到切片前面) if idx == -1 { elemType := target.Type().Elem() newElem := reflect.New(elemType).Elem() - if !r.setValue(newElem, v) { - return false + if err := r.setValue(newElem, v); err != nil { + return err } // 将新元素放在前面,原有元素顺序后移 newSlice := reflect.MakeSlice(target.Type(), 0, target.Len()+1) newSlice = reflect.Append(newSlice, newElem) newSlice = reflect.AppendSlice(newSlice, target) target.Set(newSlice) - return true + return nil } if idx < 0 || idx >= target.Len() { - return false + return NewErrSliceIndexOutOfRange(idx, target.Len()) } elem := target.Index(idx) if !elem.CanSet() { - return false + return NewErrSliceElementCannotSet(idx) } return r.setValue(elem, v) case reflect.Array: idx, err := strconv.Atoi(key) - if err != nil || idx < 0 || idx >= target.Len() { - return false + if err != nil { + return NewErrInvalidArrayIndex(key, err) + } + if idx < 0 || idx >= target.Len() { + return NewErrArrayIndexOutOfRange(idx, target.Len()) } elem := target.Index(idx) if !elem.CanSet() { - return false + return NewErrArrayElementCannotSet(idx) } return r.setValue(elem, v) } - return false + return NewErrUnsupportedTargetType(target.Kind()) } // setValue 设置值的辅助方法 // 使用 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) if err != nil { - return false + return NewErrNormalizeInputFailed(err) } targetType := field.Type() @@ -317,7 +325,7 @@ func (r *rfx) setValue(field reflect.Value, v any) bool { // 尝试直接赋值(类型完全匹配) if val.Type().AssignableTo(targetType) { field.Set(val) - return true + return nil } // 如果源值是指针但目标不是指针,尝试解引用后再赋值 @@ -325,7 +333,7 @@ func (r *rfx) setValue(field reflect.Value, v any) bool { derefVal := val.Elem() if derefVal.Type().AssignableTo(targetType) { field.Set(derefVal) - return true + return nil } // 解引用后继续使用下面的逻辑处理 val = derefVal @@ -337,7 +345,7 @@ func (r *rfx) setValue(field reflect.Value, v any) bool { // 如果传入的值已经是指针类型,尝试直接赋值 if val.Type().AssignableTo(targetType) { 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) // 递归设置指针指向的值 - if !r.setValue(newPtr.Elem(), v) { - return false + if err := r.setValue(newPtr.Elem(), v); err != nil { + return err } field.Set(newPtr) - return true + return nil case reflect.Slice: // 处理切片类型,支持从通用切片(如 []any)转换 if val.Kind() != reflect.Slice && val.Kind() != reflect.Array { - return false + return NewErrCannotConvertToSlice(val.Type(), targetType) } newSlice := reflect.MakeSlice(targetType, val.Len(), val.Len()) for i := 0; i < val.Len(); i++ { - if !r.setValue(newSlice.Index(i), val.Index(i)) { - return false + if err := r.setValue(newSlice.Index(i), val.Index(i).Interface()); err != nil { + return NewErrSetSliceElementFailed(i, err) } } field.Set(newSlice) - return true + return nil case reflect.Struct: // 遍历目标结构体的字段,从源值(结构体或 map)中按字段名取值并设置 // 仅支持从 struct 或 map 填充 if val.Kind() != reflect.Struct && val.Kind() != reflect.Map { - return false + return NewErrCannotConvertToStruct(val.Type(), targetType) } fieldType := field.Type() @@ -390,11 +398,11 @@ func (r *rfx) setValue(field reflect.Value, v any) bool { } // 使用 setValue 复用现有的类型转换逻辑,忽略单个字段失败 - if !r.setValue(dstField, valField) { - return false + if err := r.setValue(dstField, valField.Interface()); err != nil { + 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: converted, err = cast.ToStringE(v) default: - return false + return NewErrUnsupportedTargetTypeForValue(targetType, fmt.Sprintf("%T", v)) } if err != nil { // 如果 cast 失败,尝试标准的反射类型转换作为后备 if val.IsValid() && val.Type().ConvertibleTo(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)) - return true + return nil } // Delete 删除指定路径的值,支持链式调用 // 如果删除失败会 panic func (r *rfx) Delete(p ...string) R { if len(p) == 0 { - panic("rfx: empty path") + panic(ErrEmptyPath) } // 展开路径 @@ -970,8 +978,8 @@ func (r *rfx) UnmarshalJSON(data []byte) error { } // 如果类型不匹配,尝试使用 setValue 进行转换 - if !r.setValue(target, v) { - return fmt.Errorf("rfx: failed to unmarshal JSON into type %s", target.Type()) + if err := r.setValue(target, v); err != nil { + return NewErrUnmarshalFailed(target.Type(), err) } return nil diff --git a/util.go b/util.go index 4e9f094..8f81b28 100644 --- a/util.go +++ b/util.go @@ -360,7 +360,7 @@ func normalizeInputValue(v any) (reflect.Value, bool, error) { // 如果所有元素都是 nil 或无效,退回到通用处理 if elemType == nil { - rv = reflect.ValueOf(v) + rv = reflect.ValueOf(nil) } else { isPtr = true count := len(vv)