Files
2026-09-26 16:32:40 +03:00

355 lines
10 KiB
Go

package schema
import (
"reflect"
"unsafe"
"aego/core/errs"
"aego/core/ids"
"aego/core/log"
)
func badValue(field string, got, want Kind) error {
return errs.New(errs.SchemaBadValue, "value kind does not match the field",
log.F("field", field), log.F("got", got.String()), log.F("want", want.String()))
}
func (r *Registry) makeAccessors(fd *FieldDescriptor, ft reflect.Type, off uintptr) error {
name := fd.Name
switch fd.Kind {
case KindBool:
fd.Get = func(p unsafe.Pointer, dst *Value) {
*dst = Bool(*(*bool)(unsafe.Add(p, off)))
}
fd.Set = func(p unsafe.Pointer, v Value) error {
if v.Kind != KindBool {
return badValue(name, v.Kind, KindBool)
}
*(*bool)(unsafe.Add(p, off)) = v.I != 0
return nil
}
case KindInt32:
fd.Get = func(p unsafe.Pointer, dst *Value) {
*dst = Int32(*(*int32)(unsafe.Add(p, off)))
}
fd.Set = func(p unsafe.Pointer, v Value) error {
*(*int32)(unsafe.Add(p, off)) = int32(v.AsInt())
return nil
}
case KindInt64:
fd.Get = func(p unsafe.Pointer, dst *Value) {
*dst = Int64(*(*int64)(unsafe.Add(p, off)))
}
fd.Set = func(p unsafe.Pointer, v Value) error {
*(*int64)(unsafe.Add(p, off)) = v.AsInt()
return nil
}
case KindFloat32:
fd.Get = func(p unsafe.Pointer, dst *Value) {
*dst = Float32(*(*float32)(unsafe.Add(p, off)))
}
fd.Set = func(p unsafe.Pointer, v Value) error {
*(*float32)(unsafe.Add(p, off)) = float32(v.AsFloat())
return nil
}
case KindFloat64:
fd.Get = func(p unsafe.Pointer, dst *Value) {
*dst = Float64(*(*float64)(unsafe.Add(p, off)))
}
fd.Set = func(p unsafe.Pointer, v Value) error {
*(*float64)(unsafe.Add(p, off)) = v.AsFloat()
return nil
}
case KindVec2:
fd.Get = func(p unsafe.Pointer, dst *Value) {
*dst = Vec2(*(*vec2)(unsafe.Add(p, off)))
}
fd.Set = func(p unsafe.Pointer, v Value) error {
if v.Kind != KindVec2 {
return badValue(name, v.Kind, KindVec2)
}
*(*vec2)(unsafe.Add(p, off)) = v.V
return nil
}
case KindColor:
fd.Get = func(p unsafe.Pointer, dst *Value) {
*dst = Color(*(*color)(unsafe.Add(p, off)))
}
fd.Set = func(p unsafe.Pointer, v Value) error {
if v.Kind != KindColor {
return badValue(name, v.Kind, KindColor)
}
*(*color)(unsafe.Add(p, off)) = v.C
return nil
}
case KindRect:
fd.Get = func(p unsafe.Pointer, dst *Value) {
*dst = Rect(*(*rect)(unsafe.Add(p, off)))
}
fd.Set = func(p unsafe.Pointer, v Value) error {
if v.Kind != KindRect {
return badValue(name, v.Kind, KindRect)
}
*(*rect)(unsafe.Add(p, off)) = v.R
return nil
}
case KindString:
fd.Get = func(p unsafe.Pointer, dst *Value) {
*dst = String(*(*string)(unsafe.Add(p, off)))
}
fd.Set = func(p unsafe.Pointer, v Value) error {
if v.Kind != KindString {
return badValue(name, v.Kind, KindString)
}
*(*string)(unsafe.Add(p, off)) = v.S
return nil
}
case KindEnum:
return makeEnumAccessors(fd, ft, off)
case KindAssetRef:
fd.Get = func(p unsafe.Pointer, dst *Value) {
ref := reflect.NewAt(ft, unsafe.Add(p, off)).Interface().(AssetRef)
*dst = Asset(ref.AssetRefID())
}
fd.Set = func(p unsafe.Pointer, v Value) error {
if v.Kind != KindAssetRef {
return badValue(name, v.Kind, KindAssetRef)
}
ref := reflect.NewAt(ft, unsafe.Add(p, off)).Interface().(AssetRef)
ref.SetAssetRefID(v.Asset)
return nil
}
case KindNodeRef:
fd.Get = func(p unsafe.Pointer, dst *Value) {
ref := reflect.NewAt(ft, unsafe.Add(p, off)).Interface().(NodeRef)
*dst = Node(ref.NodeRefID())
}
fd.Set = func(p unsafe.Pointer, v Value) error {
if v.Kind != KindNodeRef {
return badValue(name, v.Kind, KindNodeRef)
}
ref := reflect.NewAt(ft, unsafe.Add(p, off)).Interface().(NodeRef)
ref.SetNodeRefID(v.Node)
return nil
}
case KindList:
return r.makeListAccessors(fd, ft, off)
case KindStruct:
return r.makeStructAccessors(fd, ft, off)
default:
return errs.New(errs.SchemaBadField, "field kind has no accessor", log.F("field", name))
}
return nil
}
func makeEnumAccessors(fd *FieldDescriptor, ft reflect.Type, off uintptr) error {
switch ft.Kind() {
case reflect.Int8:
fd.Get = func(p unsafe.Pointer, dst *Value) { *dst = Enum(int64(*(*int8)(unsafe.Add(p, off)))) }
fd.Set = func(p unsafe.Pointer, v Value) error {
*(*int8)(unsafe.Add(p, off)) = int8(v.AsInt())
return nil
}
case reflect.Int16:
fd.Get = func(p unsafe.Pointer, dst *Value) { *dst = Enum(int64(*(*int16)(unsafe.Add(p, off)))) }
fd.Set = func(p unsafe.Pointer, v Value) error {
*(*int16)(unsafe.Add(p, off)) = int16(v.AsInt())
return nil
}
case reflect.Int32:
fd.Get = func(p unsafe.Pointer, dst *Value) { *dst = Enum(int64(*(*int32)(unsafe.Add(p, off)))) }
fd.Set = func(p unsafe.Pointer, v Value) error {
*(*int32)(unsafe.Add(p, off)) = int32(v.AsInt())
return nil
}
case reflect.Int64, reflect.Int:
fd.Get = func(p unsafe.Pointer, dst *Value) { *dst = Enum(*(*int64)(unsafe.Add(p, off))) }
fd.Set = func(p unsafe.Pointer, v Value) error {
*(*int64)(unsafe.Add(p, off)) = v.AsInt()
return nil
}
case reflect.Uint8:
fd.Get = func(p unsafe.Pointer, dst *Value) { *dst = Enum(int64(*(*uint8)(unsafe.Add(p, off)))) }
fd.Set = func(p unsafe.Pointer, v Value) error {
*(*uint8)(unsafe.Add(p, off)) = uint8(v.AsInt())
return nil
}
case reflect.Uint16:
fd.Get = func(p unsafe.Pointer, dst *Value) { *dst = Enum(int64(*(*uint16)(unsafe.Add(p, off)))) }
fd.Set = func(p unsafe.Pointer, v Value) error {
*(*uint16)(unsafe.Add(p, off)) = uint16(v.AsInt())
return nil
}
case reflect.Uint32:
fd.Get = func(p unsafe.Pointer, dst *Value) { *dst = Enum(int64(*(*uint32)(unsafe.Add(p, off)))) }
fd.Set = func(p unsafe.Pointer, v Value) error {
*(*uint32)(unsafe.Add(p, off)) = uint32(v.AsInt())
return nil
}
default:
return errs.New(errs.SchemaBadField, "enum must have an integer underlying type",
log.F("field", fd.Name), log.F("kind", ft.Kind().String()))
}
return nil
}
func (r *Registry) makeListAccessors(fd *FieldDescriptor, ft reflect.Type, off uintptr) error {
elem := ft.Elem()
elemKind := fd.Elem
structID := fd.Struct
fd.Get = func(p unsafe.Pointer, dst *Value) {
rv := reflect.NewAt(ft, unsafe.Add(p, off)).Elem()
out := Value{Kind: KindList}
if rv.Len() == 0 {
*dst = out
return
}
out.List = make([]Value, rv.Len())
for i := 0; i < rv.Len(); i++ {
out.List[i] = r.valueFrom(rv.Index(i), elemKind, structID)
}
*dst = out
}
fd.Set = func(p unsafe.Pointer, v Value) error {
if v.Kind != KindList {
return badValue(fd.Name, v.Kind, KindList)
}
rv := reflect.NewAt(ft, unsafe.Add(p, off)).Elem()
slice := reflect.MakeSlice(ft, len(v.List), len(v.List))
for i := range v.List {
if err := r.assignTo(slice.Index(i), v.List[i], elemKind, structID, elem); err != nil {
return err
}
}
rv.Set(slice)
return nil
}
return nil
}
func (r *Registry) makeStructAccessors(fd *FieldDescriptor, ft reflect.Type, off uintptr) error {
id := fd.Struct
fd.Get = func(p unsafe.Pointer, dst *Value) {
td, ok := r.ByID(id)
if !ok {
*dst = Value{Kind: KindStruct}
return
}
inner := unsafe.Add(p, off)
obj := &Object{Type: id, Fields: make([]Value, len(td.Fields))}
for i := range td.Fields {
td.Fields[i].Get(inner, &obj.Fields[i])
}
*dst = Value{Kind: KindStruct, Obj: obj}
}
fd.Set = func(p unsafe.Pointer, v Value) error {
if v.Kind != KindStruct || v.Obj == nil {
return badValue(fd.Name, v.Kind, KindStruct)
}
td, ok := r.ByID(id)
if !ok {
return errs.New(errs.SchemaUnknownType, "nested struct type is not registered",
log.F("field", fd.Name))
}
inner := unsafe.Add(p, off)
n := min(len(td.Fields), len(v.Obj.Fields))
for i := 0; i < n; i++ {
if err := td.Fields[i].Set(inner, v.Obj.Fields[i]); err != nil {
return err
}
}
return nil
}
return nil
}
func (r *Registry) valueFrom(rv reflect.Value, k Kind, structID TypeID) Value {
switch k {
case KindBool:
return Bool(rv.Bool())
case KindInt32:
return Int32(int32(rv.Int()))
case KindInt64:
return Int64(rv.Int())
case KindFloat32:
return Float32(float32(rv.Float()))
case KindFloat64:
return Float64(rv.Float())
case KindString:
return String(rv.String())
case KindEnum:
if rv.CanInt() {
return Enum(rv.Int())
}
return Enum(int64(rv.Uint()))
case KindVec2:
return Vec2(rv.Interface().(vec2))
case KindColor:
return Color(rv.Interface().(color))
case KindRect:
return Rect(rv.Interface().(rect))
case KindAssetRef:
return Asset(rv.Addr().Interface().(AssetRef).AssetRefID())
case KindNodeRef:
return Node(rv.Addr().Interface().(NodeRef).NodeRefID())
case KindStruct:
td, ok := r.ByID(structID)
if !ok {
return Value{Kind: KindStruct}
}
p := rv.Addr().UnsafePointer()
obj := &Object{Type: structID, Fields: make([]Value, len(td.Fields))}
for i := range td.Fields {
td.Fields[i].Get(p, &obj.Fields[i])
}
return Value{Kind: KindStruct, Obj: obj}
}
return Value{}
}
func (r *Registry) assignTo(rv reflect.Value, v Value, k Kind, structID TypeID, rt reflect.Type) error {
switch k {
case KindBool:
rv.SetBool(v.I != 0)
case KindInt32, KindInt64:
rv.SetInt(v.AsInt())
case KindFloat32, KindFloat64:
rv.SetFloat(v.AsFloat())
case KindString:
rv.SetString(v.S)
case KindEnum:
if rv.CanInt() {
rv.SetInt(v.AsInt())
} else {
rv.SetUint(uint64(v.AsInt()))
}
case KindVec2:
rv.Set(reflect.ValueOf(v.V))
case KindColor:
rv.Set(reflect.ValueOf(v.C))
case KindRect:
rv.Set(reflect.ValueOf(v.R))
case KindAssetRef:
rv.Addr().Interface().(AssetRef).SetAssetRefID(v.Asset)
case KindNodeRef:
rv.Addr().Interface().(NodeRef).SetNodeRefID(v.Node)
case KindStruct:
td, ok := r.ByID(structID)
if !ok || v.Obj == nil {
return errs.New(errs.SchemaBadValue, "cannot assign struct element")
}
p := rv.Addr().UnsafePointer()
n := min(len(td.Fields), len(v.Obj.Fields))
for i := 0; i < n; i++ {
if err := td.Fields[i].Set(p, v.Obj.Fields[i]); err != nil {
return err
}
}
default:
return errs.New(errs.SchemaBadValue, "unsupported list element kind", log.F("kind", k.String()))
}
return nil
}
var _ = ids.AssetID{}