reflux/fieldx/schema.go
what b73099d205 refactor: 改进错误处理,使用预定义错误替代硬编码字符串
- fieldx.Schema.Generate: 添加 panic 捕获机制,优雅处理 reflux.New 的异常
- fieldx.Schema.Generate: 支持 any 类型参数,增强通用性
- reflux.New: 使用预定义错误(ErrInvalidValue, ErrTargetNilPointer, NewErrUnsupportedTargetType)替代硬编码错误字符串
- 提高错误信息的一致性和可读性
2025-12-26 21:15:31 +08:00

169 lines
4.1 KiB
Go

package fieldx
import (
"encoding/json"
"fmt"
"strings"
"git.fsdpf.net/go/reflux"
)
// FieldType 定义字段类型
type FieldType string
const (
// FieldTypeString 固定字符串值
FieldTypeString FieldType = "string"
// FieldTypeField 从传入对象中获取字段值
FieldTypeField FieldType = "field"
// FieldTypeObject 嵌套对象类型
FieldTypeObject FieldType = "object"
)
// Field 定义单个字段的 Schema
type Field struct {
// Type 字段类型: string, field, object
Type FieldType `json:"type"`
// Value 字段值,根据 Type 不同含义不同:
// - string: 固定字符串值
// - field: 要获取的字段路径
// - object: 忽略,使用 Fields
Value string `json:"value,omitempty"`
// Fields 当 Type 为 object 时,包含嵌套的字段定义
Fields Schema `json:"fields,omitempty"`
}
// Schema 定义字段映射表
type Schema map[string]Field
// Generate 根据 Schema 生成对象
// source: 源数据对象,用于 FieldTypeField 类型获取字段值
// 返回生成的 map[string]any 对象,如果 source 无效或处理失败则返回错误
func (s Schema) Generate(source any) (result map[string]any, err error) {
// 捕获 reflux.New 可能产生的 panic
defer func() {
if r := recover(); r != nil {
result = nil
err = fmt.Errorf("failed to wrap source data: %v", r)
}
}()
// 使用 reflux 包装源数据,提供统一的访问接口
rfx := reflux.New(source)
result = make(map[string]any)
err = s.generateFields(rfx, result)
return result, err
}
// generateFields 递归生成字段
func (s Schema) generateFields(source reflux.R, target map[string]any) error {
for fieldName, field := range s {
value, err := field.generateValue(source)
if err != nil {
return fmt.Errorf("failed to generate field %s: %w", fieldName, err)
}
target[fieldName] = value
}
return nil
}
// generateValue 生成字段值
func (f Field) generateValue(source reflux.R) (any, error) {
switch f.Type {
case FieldTypeString:
return f.Value, nil
case FieldTypeField:
return getFieldValue(f.Value, source)
case FieldTypeObject:
// 嵌套对象
nestedResult := make(map[string]any)
err := f.Fields.generateFields(source, nestedResult)
if err != nil {
return nil, err
}
return nestedResult, nil
default:
return nil, fmt.Errorf("unsupported field type: %s", f.Type)
}
}
// getFieldValue 从源对象获取字段值
// 支持点号分隔的路径,如 "user.name"
func getFieldValue(path string, source reflux.R) (any, error) {
// 分割路径
parts := strings.Split(path, ".")
// 检查字段是否存在
if !source.Exists(parts...) {
return nil, fmt.Errorf("field not found: %s", path)
}
// 获取值
accessor := source.Get(parts...)
return accessor.Any(), nil
}
// SchemaFromJSON 从 JSON 字符串创建 Schema
// 这是最简单直接的方式
func SchemaFromJSON(jsonStr string) (Schema, error) {
var schema Schema
err := json.Unmarshal([]byte(jsonStr), &schema)
if err != nil {
return nil, fmt.Errorf("failed to unmarshal JSON: %w", err)
}
return schema, nil
}
// SchemaFromMap 从 map[string]any 创建 Schema
// 支持从 JSON 反序列化后的 map 转换
func SchemaFromMap(data map[string]any) (Schema, error) {
schema := make(Schema)
for key, value := range data {
field, err := fieldFromMap(value)
if err != nil {
return nil, err
}
schema[key] = field
}
return schema, nil
}
// fieldFromMap 从 map[string]any 创建 Field
func fieldFromMap(data any) (Field, error) {
m, ok := data.(map[string]any)
if !ok {
return Field{}, fmt.Errorf("invalid field data: expected map[string]any, got %T", data)
}
field := Field{}
// 解析 type
if typeVal, ok := m["type"]; ok {
if typeStr, ok := typeVal.(string); ok {
field.Type = FieldType(typeStr)
}
}
// 解析 value
if value, ok := m["value"]; ok {
if valueStr, ok := value.(string); ok {
field.Value = valueStr
}
}
// 解析 fields (嵌套)
if fields, ok := m["fields"]; ok {
if fieldsMap, ok := fields.(map[string]any); ok {
nestedSchema, err := SchemaFromMap(fieldsMap)
if err != nil {
return Field{}, err
}
field.Fields = nestedSchema
}
}
return field, nil
}