diff --git a/rfx_test.go b/rfx_test.go index b22ddb0..2b282d2 100644 --- a/rfx_test.go +++ b/rfx_test.go @@ -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()) + } +} diff --git a/util.go b/util.go index 8f81b28..6aea61e 100644 --- a/util.go +++ b/util.go @@ -6,6 +6,8 @@ import ( "strconv" "strings" "unicode" + + "git.fsdpf.net/go/reflux/valuex" ) // DeepClone 深度克隆一个 reflect.Value @@ -335,54 +337,68 @@ func tryMapFieldKey(m reflect.Value, key string) reflect.Value { return reflect.Value{} } +// 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 slice { + // 检查是否为 nil (通过 any 转换检查) + if any(it) == nil { + continue + } + val := it.Raw() + if val.IsValid() { + elemType = val.Type() + break + } + } + + // 如果所有元素都是 nil 或无效,返回 nil + if elemType == 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() + if !val.IsValid() { + continue + } + if !val.Type().AssignableTo(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 +} + // 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 { + switch vv := v.(type) { + case R: isPtr = true rv = vv.Raw() - } else if vv, ok := v.([]R); ok { - // 支持直接传入 []R,自动转换为底层切片 - var elemType reflect.Type - for _, it := range vv { - if it == nil { - continue - } - val := it.Raw() - if val.IsValid() { - elemType = val.Type() - break - } - } - - // 如果所有元素都是 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 { - continue - } - val := it.Raw() - if !val.IsValid() { - 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) - } - rv.Index(i).Set(val) - } - } - } else if vv, ok := v.(reflect.Value); ok { + 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) } diff --git a/valuex/accessor.go b/valuex/accessor.go index e7a12c7..65b0a3b 100644 --- a/valuex/accessor.go +++ b/valuex/accessor.go @@ -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 diff --git a/valuex/nil_accessor.go b/valuex/nil_accessor.go index 4cb2d07..b8eed78 100644 --- a/valuex/nil_accessor.go +++ b/valuex/nil_accessor.go @@ -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