This is an automated email from the git hooks/post-receive script.
git pushed a commit to branch main
in repository ego.
View the commit online.
commit ced4b1cca9c3dda40e6bb9a71ef59038a3f7c6ab
Author: [email protected] <[email protected]>
AuthorDate: Tue Mar 31 13:41:17 2026 -0600
feat(efl): add struct marshal/unmarshal with eina tags
ValueMarshal now handles Go structs → EINA_VALUE_TYPE_STRUCT,
building C struct descriptors dynamically from reflection.
ValueUnmarshal reverses the conversion. Supports eina:"name"
tags, unexported field skipping, eina:"-" skip, and nested structs.
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
---
efl/value.go | 16 +-
efl/value_marshal.go | 13 +-
efl/value_marshal_test.go | 106 ++++++++
efl/value_struct.go | 627 ++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 754 insertions(+), 8 deletions(-)
diff --git a/efl/value.go b/efl/value.go
index 3018bca..35ef26e 100644
--- a/efl/value.go
+++ b/efl/value.go
@@ -91,9 +91,14 @@ type ScalarType interface {
// Value is a Go wrapper around a C Eina_Value scalar.
// When owned is true, Free releases the underlying C allocation.
+//
+// structDescs is non-nil only for struct-typed Values. It collects all
+// heap-allocated Eina_Value_Struct_Desc pointers (own plus all transitively
+// embedded nested struct descriptors) that must outlive the Value.
type Value struct {
- ptr *C.Eina_Value
- owned bool
+ ptr *C.Eina_Value
+ owned bool
+ structDescs []unsafe.Pointer
}
// NewValue creates a new Eina_Value of the type inferred from T and sets it to
@@ -360,6 +365,13 @@ func (v *Value) Free() {
}
C._ego_value_free(v.ptr)
v.ptr = nil
+ // Struct-typed values carry heap-allocated Eina_Value_Struct_Descs (own
+ // plus all transitively nested struct descriptors) that must be released
+ // after the value is freed.
+ for _, desc := range v.structDescs {
+ freeStructDesc(desc)
+ }
+ v.structDescs = nil
}
// IsNil reports whether the underlying C pointer is nil.
diff --git a/efl/value_marshal.go b/efl/value_marshal.go
index 947df13..d334bd5 100644
--- a/efl/value_marshal.go
+++ b/efl/value_marshal.go
@@ -6,11 +6,12 @@ import (
"reflect"
)
-// ValueMarshal converts any Go scalar value to a *Value.
+// ValueMarshal converts a Go value to a *Value.
//
-// Supported input types: bool, string, all integer and float variants, and
-// *Value (pass-through). Slice, map, and struct inputs return a "not yet
-// implemented" error. nil returns an error.
+// Supported input types: bool, string, all integer and float variants,
+// *Value (pass-through), slices with scalar or nested elements, maps with
+// string keys, and structs (marshalled via Eina_Value_Struct). nil returns
+// an error.
func ValueMarshal(v any) (*Value, error) {
if v == nil {
return nil, errors.New("efl: ValueMarshal: nil input")
@@ -60,7 +61,7 @@ func ValueMarshal(v any) (*Value, error) {
case reflect.Map:
return marshalMap(rv)
case reflect.Struct:
- return nil, errors.New("efl: ValueMarshal: struct: not yet implemented")
+ return marshalStruct(rv)
default:
return nil, fmt.Errorf("efl: ValueMarshal: unsupported type %T", v)
}
@@ -194,7 +195,7 @@ func ValueUnmarshal(val *Value, dst any) error {
return unmarshalMap(val, elem)
case reflect.Struct:
- return errors.New("efl: ValueUnmarshal: struct: not yet implemented")
+ return unmarshalStruct(val, elem)
default:
return fmt.Errorf("efl: ValueUnmarshal: unsupported destination type %T", dst)
diff --git a/efl/value_marshal_test.go b/efl/value_marshal_test.go
index 03493cc..31a549a 100644
--- a/efl/value_marshal_test.go
+++ b/efl/value_marshal_test.go
@@ -235,3 +235,109 @@ func TestMarshalMapStringInt(t *testing.T) {
t.Errorf("got %v", got)
}
}
+
+func TestMarshalStruct(t *testing.T) {
+ type Config struct {
+ FontSize int `eina:"font_size"`
+ Theme string `eina:"theme"`
+ Scale float64 `eina:"scale"`
+ }
+ v, err := ValueMarshal(Config{FontSize: 14, Theme: "dark", Scale: 1.5})
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer v.Free()
+ var got Config
+ if err := ValueUnmarshal(v, &got); err != nil {
+ t.Fatal(err)
+ }
+ if got.FontSize != 14 {
+ t.Errorf("FontSize=%d", got.FontSize)
+ }
+ if got.Theme != "dark" {
+ t.Errorf("Theme=%q", got.Theme)
+ }
+ if got.Scale != 1.5 {
+ t.Errorf("Scale=%f", got.Scale)
+ }
+}
+
+func TestMarshalStructNoTag(t *testing.T) {
+ type Simple struct {
+ Name string
+ Count int
+ }
+ v, err := ValueMarshal(Simple{Name: "test", Count: 5})
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer v.Free()
+ var got Simple
+ if err := ValueUnmarshal(v, &got); err != nil {
+ t.Fatal(err)
+ }
+ if got.Name != "test" || got.Count != 5 {
+ t.Errorf("got %+v", got)
+ }
+}
+
+func TestMarshalStructSkipUnexported(t *testing.T) {
+ type WithPrivate struct {
+ Public int `eina:"public"`
+ private string //nolint:unused
+ }
+ v, err := ValueMarshal(WithPrivate{Public: 42})
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer v.Free()
+ var got WithPrivate
+ if err := ValueUnmarshal(v, &got); err != nil {
+ t.Fatal(err)
+ }
+ if got.Public != 42 {
+ t.Errorf("Public=%d", got.Public)
+ }
+}
+
+func TestMarshalStructSkipTag(t *testing.T) {
+ type WithSkip struct {
+ Keep int `eina:"keep"`
+ Skip string `eina:"-"`
+ }
+ v, err := ValueMarshal(WithSkip{Keep: 1, Skip: "ignored"})
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer v.Free()
+ var got WithSkip
+ if err := ValueUnmarshal(v, &got); err != nil {
+ t.Fatal(err)
+ }
+ if got.Keep != 1 {
+ t.Errorf("Keep=%d", got.Keep)
+ }
+}
+
+func TestMarshalStructNested(t *testing.T) {
+ type Inner struct {
+ X int `eina:"x"`
+ Y int `eina:"y"`
+ }
+ type Outer struct {
+ Name string `eina:"name"`
+ Point Inner `eina:"point"`
+ }
+ v, err := ValueMarshal(Outer{Name: "origin", Point: Inner{X: 10, Y: 20}})
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer v.Free()
+ var got Outer
+ if err := ValueUnmarshal(v, &got); err != nil {
+ t.Fatal(err)
+ }
+ if got.Name != "origin" || got.Point.X != 10 || got.Point.Y != 20 {
+ t.Errorf("got %+v", got)
+ }
+}
diff --git a/efl/value_struct.go b/efl/value_struct.go
new file mode 100644
index 0000000..31c0049
--- /dev/null
+++ b/efl/value_struct.go
@@ -0,0 +1,627 @@
+package efl
+
+/*
+#cgo pkg-config: eina
+
+#include <Eina.h>
+#include <stdlib.h>
+#include <string.h>
+
+// _ego_struct_desc_new allocates a heap Eina_Value_Struct_Desc for count
+// members occupying size bytes total. A NULL-terminated sentinel is appended
+// beyond the count slots so that Eina's linear-search find_member terminates
+// correctly.
+static Eina_Value_Struct_Desc *_ego_struct_desc_new(unsigned int count, unsigned int size) {
+ Eina_Value_Struct_Desc *desc = calloc(1, sizeof(Eina_Value_Struct_Desc));
+ if (!desc) return NULL;
+ desc->version = EINA_VALUE_STRUCT_DESC_VERSION;
+ desc->ops = NULL;
+ desc->member_count = count;
+ desc->size = size;
+ // count+1 slots: the extra slot is the NULL sentinel.
+ Eina_Value_Struct_Member *members = calloc(count + 1, sizeof(Eina_Value_Struct_Member));
+ if (!members) { free(desc); return NULL; }
+ members[count].name = NULL;
+ members[count].type = NULL;
+ members[count].offset = 0;
+ desc->members = members;
+ return desc;
+}
+
+// _ego_struct_desc_set_member fills slot idx of the descriptor. name is
+// copied so the caller may release its C string immediately.
+static void _ego_struct_desc_set_member(Eina_Value_Struct_Desc *desc,
+ unsigned int idx,
+ const char *name,
+ const Eina_Value_Type *type,
+ unsigned int offset) {
+ Eina_Value_Struct_Member *m = (Eina_Value_Struct_Member *)&desc->members[idx];
+ m->name = strdup(name);
+ m->type = type;
+ m->offset = offset;
+}
+
+// _ego_struct_desc_free releases a descriptor allocated by _ego_struct_desc_new.
+static void _ego_struct_desc_free(Eina_Value_Struct_Desc *desc) {
+ if (!desc) return;
+ for (unsigned int i = 0; i < desc->member_count; i++) {
+ free((void *)desc->members[i].name);
+ }
+ free((void *)desc->members);
+ free(desc);
+}
+
+// _ego_value_struct_new is a non-inline wrapper around eina_value_struct_new.
+static Eina_Value *_ego_value_struct_new(const Eina_Value_Struct_Desc *desc) {
+ return eina_value_struct_new(desc);
+}
+
+// _ego_value_struct_pset is a non-inline wrapper around eina_value_struct_pset.
+static Eina_Bool _ego_value_struct_pset(Eina_Value *v, const char *name, const void *ptr) {
+ return eina_value_struct_pset(v, name, ptr);
+}
+
+// _ego_value_struct_pget is a non-inline wrapper around eina_value_struct_pget.
+static Eina_Bool _ego_value_struct_pget(const Eina_Value *v, const char *name, void *ptr) {
+ return eina_value_struct_pget(v, name, ptr);
+}
+
+// _ego_value_struct_set_string stores a string member. The double indirection
+// is required because EINA_VALUE_TYPE_STRING stores a char* and pset expects a
+// pointer to that pointer.
+static Eina_Bool _ego_value_struct_set_string(Eina_Value *v, const char *name, const char *str) {
+ return eina_value_struct_pset(v, name, &str);
+}
+
+// _ego_value_struct_get_string retrieves a string member. Returns an internal
+// pointer owned by the Eina_Value; the caller must not free it.
+static const char *_ego_value_struct_get_string(const Eina_Value *v, const char *name) {
+ const char *s = NULL;
+ eina_value_struct_pget(v, name, &s);
+ return s;
+}
+
+// _ego_value_struct_desc wraps the inline eina_value_struct_desc_get.
+static const Eina_Value_Struct_Desc *_ego_value_struct_desc(const Eina_Value *v) {
+ return eina_value_struct_desc_get(v);
+}
+
+// _ego_value_struct_value_set is a non-inline wrapper for the inline
+// eina_value_struct_value_set. Eina copies the src Eina_Value into the named
+// member slot; src may be freed by the caller afterwards.
+static Eina_Bool _ego_value_struct_value_set(Eina_Value *v, const char *name, const Eina_Value *src) {
+ return eina_value_struct_value_set(v, name, src);
+}
+
+// _ego_value_struct_value_get allocates and returns a fresh heap Eina_Value
+// that is a copy of the named nested member. Returns NULL on failure.
+// The caller must free with eina_value_free.
+static Eina_Value *_ego_value_struct_value_get_new(const Eina_Value *v, const char *name) {
+ Eina_Value dst;
+ memset(&dst, 0, sizeof(dst));
+ if (!eina_value_struct_value_get(v, name, &dst)) return NULL;
+ // dst is stack-allocated; copy it to a heap Eina_Value for Go to own.
+ Eina_Value *out = eina_value_new(dst.type);
+ if (!out) { eina_value_flush(&dst); return NULL; }
+ if (!eina_value_copy(&dst, out)) {
+ eina_value_free(out);
+ eina_value_flush(&dst);
+ return NULL;
+ }
+ eina_value_flush(&dst);
+ return out;
+}
+
+// Forward declaration: _ego_value_free is defined in value.go's preamble and
+// releases a heap Eina_Value. We use it here to free the inner struct value
+// after embedding it into the outer struct via eina_value_struct_value_set.
+static void _ego_value_free(Eina_Value *v);
+*/
+import "C"
+
+import (
+ "fmt"
+ "reflect"
+ "strings"
+ "unsafe"
+)
+
+// freeStructDesc releases the Eina_Value_Struct_Desc pointed to by p.
+// It is called from Value.Free() which lives in value.go.
+func freeStructDesc(p unsafe.Pointer) {
+ C._ego_struct_desc_free((*C.Eina_Value_Struct_Desc)(p))
+}
+
+// ---------------------------------------------------------------------------
+// Layout helpers
+// ---------------------------------------------------------------------------
+
+// einaFieldSize returns the byte size of the C representation for a given
+// Eina_Value_Type within an Eina_Value_Struct memory block.
+//
+// For compound types (STRUCT, ARRAY, HASH) Eina stores the runtime descriptor
+// struct (Eina_Value_Struct, Eina_Value_Array, Eina_Value_Hash) inline in the
+// outer struct's memory block. Each of these is two pointers wide (a type/desc
+// pointer and a data pointer) on all supported platforms.
+func einaFieldSize(t *C.Eina_Value_Type) uint {
+ switch t {
+ case C.EINA_VALUE_TYPE_BOOL, C.EINA_VALUE_TYPE_CHAR:
+ return 1
+ case C.EINA_VALUE_TYPE_SHORT, C.EINA_VALUE_TYPE_USHORT:
+ return 2
+ case C.EINA_VALUE_TYPE_INT, C.EINA_VALUE_TYPE_UINT, C.EINA_VALUE_TYPE_FLOAT:
+ return 4
+ case C.EINA_VALUE_TYPE_INT64, C.EINA_VALUE_TYPE_UINT64, C.EINA_VALUE_TYPE_DOUBLE:
+ return 8
+ case C.EINA_VALUE_TYPE_STRING, C.EINA_VALUE_TYPE_STRINGSHARE:
+ // A char* pointer.
+ return uint(unsafe.Sizeof(uintptr(0)))
+ case C.EINA_VALUE_TYPE_STRUCT, C.EINA_VALUE_TYPE_ARRAY, C.EINA_VALUE_TYPE_HASH:
+ // Eina stores the inner runtime descriptor struct (two pointer-sized words).
+ return uint(unsafe.Sizeof(uintptr(0))) * 2
+ case C.EINA_VALUE_TYPE_VALUE:
+ // sizeof(Eina_Value) — type* + union (one pointer-sized word).
+ return uint(unsafe.Sizeof(uintptr(0))) * 2
+ default:
+ return uint(unsafe.Sizeof(uintptr(0)))
+ }
+}
+
+// einaFieldAlign returns the required alignment (in bytes) for a field of the
+// given type. For most types it equals the size; bool and char are 1-byte
+// aligned.
+func einaFieldAlign(t *C.Eina_Value_Type) uint {
+ switch t {
+ case C.EINA_VALUE_TYPE_BOOL, C.EINA_VALUE_TYPE_CHAR:
+ return 1
+ case C.EINA_VALUE_TYPE_SHORT, C.EINA_VALUE_TYPE_USHORT:
+ return 2
+ case C.EINA_VALUE_TYPE_INT, C.EINA_VALUE_TYPE_UINT, C.EINA_VALUE_TYPE_FLOAT:
+ return 4
+ default:
+ // 8-byte types and pointer-sized types.
+ sz := einaFieldSize(t)
+ if sz > 8 {
+ return 8
+ }
+ return sz
+ }
+}
+
+// alignUp rounds offset up to the next multiple of align.
+func alignUp(offset, align uint) uint {
+ return (offset + align - 1) &^ (align - 1)
+}
+
+// ---------------------------------------------------------------------------
+// Field metadata helpers
+// ---------------------------------------------------------------------------
+
+// structFieldName returns the Eina member name for field f:
+// - tag `eina:"-"` → skip (returns "", false)
+// - tag `eina:"name"` → "name"
+// - no tag → lowercased Go field name
+func structFieldName(f reflect.StructField) (name string, keep bool) {
+ if tag, ok := f.Tag.Lookup("eina"); ok {
+ if tag == "-" {
+ return "", false
+ }
+ // Support "name,options" syntax (forward-compat; only "name" used now).
+ if comma := strings.IndexByte(tag, ','); comma >= 0 {
+ tag = tag[:comma]
+ }
+ if tag != "" {
+ return tag, true
+ }
+ }
+ return strings.ToLower(f.Name), true
+}
+
+// goKindToEinaType maps a Go reflect.Kind (for scalar fields) to the
+// corresponding *C.Eina_Value_Type. Returns nil for non-scalar kinds.
+func goKindToEinaType(k reflect.Kind) *C.Eina_Value_Type {
+ switch k {
+ case reflect.Int, reflect.Int32:
+ return C.EINA_VALUE_TYPE_INT
+ case reflect.Uint, reflect.Uint32:
+ return C.EINA_VALUE_TYPE_UINT
+ case reflect.Int64:
+ return C.EINA_VALUE_TYPE_INT64
+ case reflect.Uint64:
+ return C.EINA_VALUE_TYPE_UINT64
+ case reflect.Int16:
+ return C.EINA_VALUE_TYPE_SHORT
+ case reflect.Uint16:
+ return C.EINA_VALUE_TYPE_USHORT
+ case reflect.Float32:
+ return C.EINA_VALUE_TYPE_FLOAT
+ case reflect.Float64:
+ return C.EINA_VALUE_TYPE_DOUBLE
+ case reflect.String:
+ return C.EINA_VALUE_TYPE_STRING
+ case reflect.Bool:
+ return C.EINA_VALUE_TYPE_BOOL
+ case reflect.Uint8: // byte
+ return C.EINA_VALUE_TYPE_CHAR
+ case reflect.Int8:
+ // int8 is promoted to int for Eina (matches NewValue behavior).
+ return C.EINA_VALUE_TYPE_INT
+ default:
+ return nil
+ }
+}
+
+// nestedEinaType returns the Eina_Value_Type constant for a nested (non-scalar)
+// Go kind: Struct → EINA_VALUE_TYPE_STRUCT, Slice → EINA_VALUE_TYPE_ARRAY,
+// Map → EINA_VALUE_TYPE_HASH. Returns nil for other kinds.
+func nestedEinaType(k reflect.Kind) *C.Eina_Value_Type {
+ switch k {
+ case reflect.Struct:
+ return C.EINA_VALUE_TYPE_STRUCT
+ case reflect.Slice:
+ return C.EINA_VALUE_TYPE_ARRAY
+ case reflect.Map:
+ return C.EINA_VALUE_TYPE_HASH
+ default:
+ return nil
+ }
+}
+
+// ---------------------------------------------------------------------------
+// marshalStruct
+// ---------------------------------------------------------------------------
+
+// marshalStruct converts a Go struct value (rv must be reflect.Struct kind)
+// into a *Value of Eina type EINA_VALUE_TYPE_STRUCT.
+func marshalStruct(rv reflect.Value) (*Value, error) {
+ rt := rv.Type()
+ nFields := rt.NumField()
+
+ // First pass: collect active fields and determine offsets/total size.
+ type fieldInfo struct {
+ name string
+ idx int
+ einaType *C.Eina_Value_Type
+ offset uint
+ isNested bool // true for struct/slice/map fields
+ }
+
+ fields := make([]fieldInfo, 0, nFields)
+ var cursor uint
+
+ for i := range nFields {
+ f := rt.Field(i)
+ if !f.IsExported() {
+ continue
+ }
+ name, keep := structFieldName(f)
+ if !keep {
+ continue
+ }
+
+ k := f.Type.Kind()
+ et := goKindToEinaType(k)
+ nested := false
+ if et == nil {
+ // Struct, slice, or map: use the matching Eina container type so that
+ // eina_value_struct_value_set/get type-checks pass.
+ et = nestedEinaType(k)
+ if et == nil {
+ return nil, fmt.Errorf("efl: marshalStruct: field %q has unsupported kind %v", name, k)
+ }
+ nested = true
+ }
+
+ align := einaFieldAlign(et)
+ cursor = alignUp(cursor, align)
+ fields = append(fields, fieldInfo{
+ name: name,
+ idx: i,
+ einaType: et,
+ offset: cursor,
+ isNested: nested,
+ })
+ cursor += einaFieldSize(et)
+ }
+
+ // Total size must be at least 1 byte (Eina rejects zero-size structs).
+ totalSize := cursor
+ if totalSize == 0 {
+ totalSize = 1
+ }
+
+ count := C.uint(len(fields))
+ desc := C._ego_struct_desc_new(count, C.uint(totalSize))
+ if desc == nil {
+ return nil, fmt.Errorf("efl: marshalStruct: _ego_struct_desc_new returned nil")
+ }
+
+ // Populate descriptor members.
+ for i, fi := range fields {
+ cname := C.CString(fi.name)
+ C._ego_struct_desc_set_member(desc, C.uint(i), cname, fi.einaType, C.uint(fi.offset))
+ C.free(unsafe.Pointer(cname))
+ }
+
+ raw := C._ego_value_struct_new(desc)
+ if raw == nil {
+ C._ego_struct_desc_free(desc)
+ return nil, fmt.Errorf("efl: marshalStruct: eina_value_struct_new returned nil")
+ }
+
+ // The own descriptor is the first entry; nested descriptors are appended below.
+ val := &Value{
+ ptr: raw,
+ owned: true,
+ structDescs: []unsafe.Pointer{unsafe.Pointer(desc)},
+ }
+
+ // Second pass: set field values.
+ for _, fi := range fields {
+ fv := rv.Field(fi.idx)
+ cname := C.CString(fi.name)
+
+ if fi.isNested {
+ sub, err := ValueMarshal(fv.Interface())
+ if err != nil {
+ C.free(unsafe.Pointer(cname))
+ val.Free()
+ return nil, fmt.Errorf("efl: marshalStruct: field %q: %w", fi.name, err)
+ }
+ ok := C._ego_value_struct_value_set(raw, cname, sub.ptr)
+ C.free(unsafe.Pointer(cname))
+ if ok == 0 {
+ // Set failed; free the sub value including its descriptors.
+ if sub.owned {
+ sub.Free()
+ }
+ val.Free()
+ return nil, fmt.Errorf("efl: marshalStruct: struct_value_set failed for field %q", fi.name)
+ }
+ // The outer struct's memory block now contains a copy of the inner
+ // struct's data (allocated by _eina_value_type_struct_copy), but still
+ // references the inner struct's descriptor via a pointer. Transfer
+ // descriptor ownership to val so the descriptors outlive the outer struct.
+ if sub.owned {
+ val.structDescs = append(val.structDescs, sub.structDescs...)
+ sub.structDescs = nil // prevent double-free in the bypass below
+ // Free the inner Eina_Value + its managed memory. The outer struct
+ // holds its own independent copy so this is safe.
+ // We bypass sub.Free() to avoid freeing descriptors we just transferred.
+ C._ego_value_free(sub.ptr)
+ sub.ptr = nil
+ sub.owned = false
+ }
+ continue
+ }
+
+ // Scalar field.
+ var setErr error
+ switch fi.einaType {
+ case C.EINA_VALUE_TYPE_STRING:
+ cs := C.CString(fv.String())
+ if C._ego_value_struct_set_string(raw, cname, cs) == 0 {
+ setErr = fmt.Errorf("efl: marshalStruct: set_string failed for field %q", fi.name)
+ }
+ C.free(unsafe.Pointer(cs))
+
+ case C.EINA_VALUE_TYPE_INT:
+ cv := C.int(fv.Int())
+ if C._ego_value_struct_pset(raw, cname, unsafe.Pointer(&cv)) == 0 {
+ setErr = fmt.Errorf("efl: marshalStruct: pset(INT) failed for field %q", fi.name)
+ }
+
+ case C.EINA_VALUE_TYPE_UINT:
+ cv := C.uint(fv.Uint())
+ if C._ego_value_struct_pset(raw, cname, unsafe.Pointer(&cv)) == 0 {
+ setErr = fmt.Errorf("efl: marshalStruct: pset(UINT) failed for field %q", fi.name)
+ }
+
+ case C.EINA_VALUE_TYPE_INT64:
+ cv := C.int64_t(fv.Int())
+ if C._ego_value_struct_pset(raw, cname, unsafe.Pointer(&cv)) == 0 {
+ setErr = fmt.Errorf("efl: marshalStruct: pset(INT64) failed for field %q", fi.name)
+ }
+
+ case C.EINA_VALUE_TYPE_UINT64:
+ cv := C.uint64_t(fv.Uint())
+ if C._ego_value_struct_pset(raw, cname, unsafe.Pointer(&cv)) == 0 {
+ setErr = fmt.Errorf("efl: marshalStruct: pset(UINT64) failed for field %q", fi.name)
+ }
+
+ case C.EINA_VALUE_TYPE_SHORT:
+ cv := C.short(fv.Int())
+ if C._ego_value_struct_pset(raw, cname, unsafe.Pointer(&cv)) == 0 {
+ setErr = fmt.Errorf("efl: marshalStruct: pset(SHORT) failed for field %q", fi.name)
+ }
+
+ case C.EINA_VALUE_TYPE_USHORT:
+ cv := C.ushort(fv.Uint())
+ if C._ego_value_struct_pset(raw, cname, unsafe.Pointer(&cv)) == 0 {
+ setErr = fmt.Errorf("efl: marshalStruct: pset(USHORT) failed for field %q", fi.name)
+ }
+
+ case C.EINA_VALUE_TYPE_FLOAT:
+ cv := C.float(fv.Float())
+ if C._ego_value_struct_pset(raw, cname, unsafe.Pointer(&cv)) == 0 {
+ setErr = fmt.Errorf("efl: marshalStruct: pset(FLOAT) failed for field %q", fi.name)
+ }
+
+ case C.EINA_VALUE_TYPE_DOUBLE:
+ cv := C.double(fv.Float())
+ if C._ego_value_struct_pset(raw, cname, unsafe.Pointer(&cv)) == 0 {
+ setErr = fmt.Errorf("efl: marshalStruct: pset(DOUBLE) failed for field %q", fi.name)
+ }
+
+ case C.EINA_VALUE_TYPE_BOOL:
+ var cv C.uchar
+ if fv.Bool() {
+ cv = 1
+ }
+ if C._ego_value_struct_pset(raw, cname, unsafe.Pointer(&cv)) == 0 {
+ setErr = fmt.Errorf("efl: marshalStruct: pset(BOOL) failed for field %q", fi.name)
+ }
+
+ case C.EINA_VALUE_TYPE_CHAR:
+ cv := C.char(fv.Uint())
+ if C._ego_value_struct_pset(raw, cname, unsafe.Pointer(&cv)) == 0 {
+ setErr = fmt.Errorf("efl: marshalStruct: pset(CHAR) failed for field %q", fi.name)
+ }
+ }
+
+ C.free(unsafe.Pointer(cname))
+ if setErr != nil {
+ val.Free()
+ return nil, setErr
+ }
+ }
+
+ return val, nil
+}
+
+// ---------------------------------------------------------------------------
+// unmarshalStruct
+// ---------------------------------------------------------------------------
+
+// unmarshalStruct reads fields from the struct-typed Value val and writes them
+// into the Go struct pointed to by elem (a reflect.Value of Kind Struct).
+func unmarshalStruct(val *Value, elem reflect.Value) error {
+ rt := elem.Type()
+ nFields := rt.NumField()
+
+ for i := range nFields {
+ f := rt.Field(i)
+ if !f.IsExported() {
+ continue
+ }
+ name, keep := structFieldName(f)
+ if !keep {
+ continue
+ }
+
+ fv := elem.Field(i)
+ k := f.Type.Kind()
+ et := goKindToEinaType(k)
+
+ cname := C.CString(name)
+
+ if et == nil {
+ // Nested struct, slice, or map: retrieve via struct_value_get_new.
+ subPtr := C._ego_value_struct_value_get_new(val.ptr, cname)
+ C.free(unsafe.Pointer(cname))
+ if subPtr == nil {
+ return fmt.Errorf("efl: unmarshalStruct: struct_value_get_new failed for field %q", name)
+ }
+ sub := &Value{ptr: subPtr, owned: true}
+ dst := reflect.New(f.Type)
+ if err := ValueUnmarshal(sub, dst.Interface()); err != nil {
+ sub.Free()
+ return fmt.Errorf("efl: unmarshalStruct: field %q: %w", name, err)
+ }
+ sub.Free()
+ fv.Set(dst.Elem())
+ continue
+ }
+
+ var getErr error
+ switch et {
+ case C.EINA_VALUE_TYPE_STRING:
+ cs := C._ego_value_struct_get_string(val.ptr, cname)
+ C.free(unsafe.Pointer(cname))
+ if cs == nil {
+ // Empty string fields may return nil; treat as empty.
+ fv.SetString("")
+ continue
+ }
+ fv.SetString(C.GoString(cs))
+ continue
+
+ case C.EINA_VALUE_TYPE_INT:
+ var cv C.int
+ if C._ego_value_struct_pget(val.ptr, cname, unsafe.Pointer(&cv)) == 0 {
+ getErr = fmt.Errorf("efl: unmarshalStruct: pget(INT) failed for field %q", name)
+ } else {
+ fv.SetInt(int64(cv))
+ }
+
+ case C.EINA_VALUE_TYPE_UINT:
+ var cv C.uint
+ if C._ego_value_struct_pget(val.ptr, cname, unsafe.Pointer(&cv)) == 0 {
+ getErr = fmt.Errorf("efl: unmarshalStruct: pget(UINT) failed for field %q", name)
+ } else {
+ fv.SetUint(uint64(cv))
+ }
+
+ case C.EINA_VALUE_TYPE_INT64:
+ var cv C.int64_t
+ if C._ego_value_struct_pget(val.ptr, cname, unsafe.Pointer(&cv)) == 0 {
+ getErr = fmt.Errorf("efl: unmarshalStruct: pget(INT64) failed for field %q", name)
+ } else {
+ fv.SetInt(int64(cv))
+ }
+
+ case C.EINA_VALUE_TYPE_UINT64:
+ var cv C.uint64_t
+ if C._ego_value_struct_pget(val.ptr, cname, unsafe.Pointer(&cv)) == 0 {
+ getErr = fmt.Errorf("efl: unmarshalStruct: pget(UINT64) failed for field %q", name)
+ } else {
+ fv.SetUint(uint64(cv))
+ }
+
+ case C.EINA_VALUE_TYPE_SHORT:
+ var cv C.short
+ if C._ego_value_struct_pget(val.ptr, cname, unsafe.Pointer(&cv)) == 0 {
+ getErr = fmt.Errorf("efl: unmarshalStruct: pget(SHORT) failed for field %q", name)
+ } else {
+ fv.SetInt(int64(cv))
+ }
+
+ case C.EINA_VALUE_TYPE_USHORT:
+ var cv C.ushort
+ if C._ego_value_struct_pget(val.ptr, cname, unsafe.Pointer(&cv)) == 0 {
+ getErr = fmt.Errorf("efl: unmarshalStruct: pget(USHORT) failed for field %q", name)
+ } else {
+ fv.SetUint(uint64(cv))
+ }
+
+ case C.EINA_VALUE_TYPE_FLOAT:
+ var cv C.float
+ if C._ego_value_struct_pget(val.ptr, cname, unsafe.Pointer(&cv)) == 0 {
+ getErr = fmt.Errorf("efl: unmarshalStruct: pget(FLOAT) failed for field %q", name)
+ } else {
+ fv.SetFloat(float64(cv))
+ }
+
+ case C.EINA_VALUE_TYPE_DOUBLE:
+ var cv C.double
+ if C._ego_value_struct_pget(val.ptr, cname, unsafe.Pointer(&cv)) == 0 {
+ getErr = fmt.Errorf("efl: unmarshalStruct: pget(DOUBLE) failed for field %q", name)
+ } else {
+ fv.SetFloat(float64(cv))
+ }
+
+ case C.EINA_VALUE_TYPE_BOOL:
+ var cv C.uchar
+ if C._ego_value_struct_pget(val.ptr, cname, unsafe.Pointer(&cv)) == 0 {
+ getErr = fmt.Errorf("efl: unmarshalStruct: pget(BOOL) failed for field %q", name)
+ } else {
+ fv.SetBool(cv != 0)
+ }
+
+ case C.EINA_VALUE_TYPE_CHAR:
+ var cv C.char
+ if C._ego_value_struct_pget(val.ptr, cname, unsafe.Pointer(&cv)) == 0 {
+ getErr = fmt.Errorf("efl: unmarshalStruct: pget(CHAR) failed for field %q", name)
+ } else {
+ fv.SetUint(uint64(byte(cv)))
+ }
+ }
+
+ C.free(unsafe.Pointer(cname))
+ if getErr != nil {
+ return getErr
+ }
+ }
+
+ return nil
+}
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.