feat: 完善 normalizeInputValue 支持 valuex.Accessor 并优化代码

- 在 valuex.Accessor 接口中添加 Raw() 方法
- normalizeInputValue 支持 valuex.Accessor 和 []valuex.Accessor 类型
- 提取 normalizeAccessorSlice 泛型函数消除重复代码
- 使用 switch 语句替代 if-else 链提高可读性
- 添加相关测试用例确保功能正确性
This commit is contained in:
what 2025-12-16 16:14:46 +08:00
parent 7d17293df3
commit baad0cadfc
4 changed files with 118 additions and 38 deletions

View File

@ -2,7 +2,10 @@ package reflux
import (
"encoding/json"
"reflect"
"testing"
"git.fsdpf.net/go/reflux/valuex"
)
// 测试用的结构体
@ -1422,3 +1425,53 @@ func TestSetValueChainWithCast(t *testing.T) {
t.Errorf("Expected Score to be 95.5, got %f", user.Score)
}
}
// TestNormalizeValuexAccessor 测试 normalizeInputValue 对 valuex.Accessor 的支持
func TestNormalizeValuexAccessor(t *testing.T) {
// 创建一个 R 实例
data := map[string]any{
"name": "Alice",
"age": 30,
}
r := New(data)
// 测试 valuex.Accessor 类型
rv, isPtr, err := normalizeInputValue(r)
if err != nil {
t.Fatalf("normalizeInputValue failed: %v", err)
}
if !isPtr {
t.Error("Expected isPtr to be true for valuex.Accessor")
}
if !rv.IsValid() {
t.Error("Expected valid reflect.Value")
}
}
// TestNormalizeValuexAccessorSlice 测试 normalizeInputValue 对 []valuex.Accessor 的支持
func TestNormalizeValuexAccessorSlice(t *testing.T) {
// 创建多个 R 实例
r1 := New(map[string]any{"id": 1, "name": "Alice"})
r2 := New(map[string]any{"id": 2, "name": "Bob"})
// 创建 []valuex.Accessor
slice := []valuex.Accessor{r1, r2}
// 测试 []valuex.Accessor 类型
rv, isPtr, err := normalizeInputValue(slice)
if err != nil {
t.Fatalf("normalizeInputValue failed: %v", err)
}
if !isPtr {
t.Error("Expected isPtr to be true for []valuex.Accessor")
}
if !rv.IsValid() {
t.Error("Expected valid reflect.Value")
}
if rv.Kind() != reflect.Slice {
t.Errorf("Expected Slice kind, got %v", rv.Kind())
}
if rv.Len() != 2 {
t.Errorf("Expected slice length 2, got %d", rv.Len())
}
}

64
util.go
View File

@ -6,6 +6,8 @@ import (
"strconv"
"strings"
"unicode"
"git.fsdpf.net/go/reflux/valuex"
)
// DeepClone 深度克隆一个 reflect.Value
@ -335,20 +337,13 @@ func tryMapFieldKey(m reflect.Value, key string) reflect.Value {
return reflect.Value{}
}
// normalizeInputValue 规范化传入的任意值为 reflect.Value
// 返回对应的 reflect.Value 以及是否应被视为指针(isPtr)
func normalizeInputValue(v any) (reflect.Value, bool, error) {
var rv reflect.Value
var isPtr bool
if vv, ok := v.(R); ok {
isPtr = true
rv = vv.Raw()
} else if vv, ok := v.([]R); ok {
// 支持直接传入 []R,自动转换为底层切片
// normalizeAccessorSlice 规范化 Accessor 切片为 reflect.Value
// 这个辅助函数用于处理 []R 和 []valuex.Accessor 的共同逻辑
func normalizeAccessorSlice[T valuex.Accessor](slice []T, typeName string) (reflect.Value, bool, error) {
var elemType reflect.Type
for _, it := range vv {
if it == nil {
for _, it := range slice {
// 检查是否为 nil (通过 any 转换检查)
if any(it) == nil {
continue
}
val := it.Raw()
@ -358,15 +353,16 @@ func normalizeInputValue(v any) (reflect.Value, bool, error) {
}
}
// 如果所有元素都是 nil 或无效,退回到通用处理
// 如果所有元素都是 nil 或无效,返回 nil
if elemType == nil {
rv = reflect.ValueOf(nil)
} else {
isPtr = true
count := len(vv)
rv = reflect.MakeSlice(reflect.SliceOf(elemType), count, count)
for i, it := range vv {
if it == nil {
return reflect.ValueOf(nil), false, nil
}
// 创建底层切片
count := len(slice)
rv := reflect.MakeSlice(reflect.SliceOf(elemType), count, count)
for i, it := range slice {
if any(it) == nil {
continue
}
val := it.Raw()
@ -374,15 +370,35 @@ func normalizeInputValue(v any) (reflect.Value, bool, error) {
continue
}
if !val.Type().AssignableTo(elemType) {
return rv, isPtr, fmt.Errorf("rfx: []R element type mismatch at index %d: got %s, want %s", i, val.Type(), elemType)
return rv, true, fmt.Errorf("rfx: []%s element type mismatch at index %d: got %s, want %s", typeName, i, val.Type(), elemType)
}
rv.Index(i).Set(val)
}
return rv, true, nil
}
} else if vv, ok := v.(reflect.Value); ok {
// normalizeInputValue 规范化传入的任意值为 reflect.Value
// 返回对应的 reflect.Value 以及是否应被视为指针(isPtr)
func normalizeInputValue(v any) (reflect.Value, bool, error) {
var rv reflect.Value
var isPtr bool
switch vv := v.(type) {
case R:
isPtr = true
rv = vv.Raw()
case valuex.Accessor:
isPtr = true
rv = vv.Raw()
case []valuex.Accessor:
return normalizeAccessorSlice(vv, "valuex.Accessor")
case []R:
return normalizeAccessorSlice(vv, "R")
case reflect.Value:
// 支持直接传入 reflect.Value,避免重复封装
rv = vv
} else {
default:
rv = reflect.ValueOf(v)
}

View File

@ -1,10 +1,15 @@
package valuex
import "reflect"
// Accessor 为参数值转换相关接口
type Accessor interface {
// Lookup 根据路径查找并返回对应值的访问器
Lookup(path string) (Accessor, bool)
// Raw 返回底层的 reflect.Value
Raw() reflect.Value
// Ptr 返回指向当前值的指针
Ptr() any

View File

@ -1,5 +1,7 @@
package valuex
import "reflect"
// nilAccessor 是一个空实现,用于表示不存在或空值的访问器
// 所有转换方法都会返回对应类型的零值
type nilAccessor struct{}
@ -12,6 +14,10 @@ func (n *nilAccessor) Lookup(path string) (Accessor, bool) {
return n, false
}
func (n *nilAccessor) Raw() reflect.Value {
return reflect.Value{}
}
// Ptr 返回 nil
func (n *nilAccessor) Ptr() any {
return nil