New(nil) 之前会 panic,现在返回 Nil(新增的 R 空实现单例,所有操作都是
安全的空操作/零值,不用调用方每次都判空)。New([]R{...}) 和 Array() 遇到
元素本身已经是 R(包括 Nil)时直接透传,不再重新拿 reflect.Value 包一层,
避免丢失原有的空值语义或类型信息。
121 lines
3.2 KiB
Go
121 lines
3.2 KiB
Go
package reflux
|
|
|
|
import (
|
|
"encoding/json"
|
|
"reflect"
|
|
|
|
"git.fsdpf.net/go/reflux/valuex"
|
|
)
|
|
|
|
// R 提供了一个统一的接口,用于访问和操作嵌套的结构体字段、切片元素和映射值
|
|
type R interface {
|
|
valuex.Accessor
|
|
json.Marshaler
|
|
json.Unmarshaler
|
|
|
|
// Get 通过路径获取嵌套字段的值,返回一个新的 T 实例
|
|
// 参数 p 为路径片段,例如 Get("user", "profile", "name")
|
|
Get(path ...string) R
|
|
|
|
// Scope 类似 Get,但用于创建一个指定路径的作用域视图
|
|
// 后续操作将基于这个作用域进行
|
|
Scope(p ...string) R
|
|
|
|
// Set 设置指定路径的值,支持链式调用
|
|
// 参数 key 为路径(支持点号分割),v 为要设置的值
|
|
// 返回当前 R 实例以支持链式调用
|
|
// 示例: rfx.Set("name", "Alice").Set("age", 30)
|
|
Set(key string, v any) R
|
|
|
|
// Append 追加指定路径的值
|
|
// 参数 items 为要追加的值
|
|
// 返回当前 R 实例以支持链式调用
|
|
Append(items ...any) R
|
|
|
|
// Delete 删除指定路径的值
|
|
// 参数 p 为路径片段
|
|
// 返回当前 R 实例以支持链式调用
|
|
Delete(p ...string) R
|
|
|
|
// Exists 检查指定路径的值是否存在
|
|
// 参数 p 为路径片段
|
|
// 返回 true 表示存在,false 表示不存在
|
|
Exists(p ...string) bool
|
|
|
|
// Array 将当前值转换为 R 切片
|
|
// 适用于数组或切片类型的值
|
|
Array() []R
|
|
|
|
// Keys 返回当前映射或结构体的所有键名
|
|
Keys() []string
|
|
}
|
|
|
|
// New 创建一个新的 R 实例
|
|
// 参数 v 可以是指针或非指针类型
|
|
// - 如果传入指针: 将直接使用该指针,可以修改原始数据
|
|
// - 如果传入值: 会自动创建一个深度克隆的指针副本,修改不影响原始数据
|
|
// 支持的类型: map、struct、slice、array
|
|
// 也支持 interface 类型以及部分基础类型(string/bool/float),会自动解析到实际类型
|
|
// 返回一个 R 接口实例,可用于访问和操作嵌套的字段、元素和键值对
|
|
func New(v any) R {
|
|
switch t := v.(type) {
|
|
case nil:
|
|
return Nil
|
|
case R:
|
|
return t
|
|
case []R:
|
|
return &rfx{value: reflect.ValueOf(t)}
|
|
default:
|
|
if t == valuex.Nil {
|
|
return Nil
|
|
}
|
|
}
|
|
|
|
rv, isPtr, err := normalizeInputValue(v)
|
|
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
if !rv.IsValid() {
|
|
panic(ErrInvalidValue)
|
|
}
|
|
|
|
// 递归解引用指针和接口,直到获取实际的值类型
|
|
actualValue := rv
|
|
for actualValue.Kind() == reflect.Ptr || actualValue.Kind() == reflect.Interface {
|
|
if actualValue.IsNil() {
|
|
panic(ErrTargetNilPointer)
|
|
}
|
|
actualValue = actualValue.Elem()
|
|
// 如果解引用后的类型是指针,标记为指针模式
|
|
if actualValue.Kind() == reflect.Ptr {
|
|
isPtr = true
|
|
}
|
|
}
|
|
|
|
// 检查最终的实际类型是否为支持的类型
|
|
switch actualValue.Kind() {
|
|
case reflect.Map,
|
|
reflect.Struct,
|
|
reflect.Slice,
|
|
reflect.Array,
|
|
reflect.String,
|
|
reflect.Bool,
|
|
reflect.Float32,
|
|
reflect.Float64:
|
|
// 支持的类型
|
|
default:
|
|
panic(NewErrUnsupportedTargetType(actualValue.Kind()))
|
|
}
|
|
|
|
// 如果原始传入的不是指针类型,需要进行深度克隆以避免修改原始数据
|
|
// 对于引用类型(map, slice)这尤其重要
|
|
if !isPtr {
|
|
// 使用深度克隆创建一个完全独立的副本
|
|
rv = DeepClone(actualValue)
|
|
}
|
|
|
|
return &rfx{value: rv}
|
|
}
|