feat: 增强错误处理与示例
This commit is contained in:
144
rfx.go
144
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
|
||||
|
||||
Reference in New Issue
Block a user