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 3bcad7fbd11fce487eaf4340cc078786aab2e937
Author: [email protected] <[email protected]>
AuthorDate: Thu Mar 26 21:40:07 2026 -0600

    feat(eet): add marshal and unmarshal between Go structs and C shadow buffers
    
    Implements bidirectional serialization between Go struct values and EET's C
    shadow buffer representation. MarshalStruct converts a Go struct into a C
    buffer layout compatible with EET descriptors, handling all types including
    nested structs (inline and pointer), slices (as VAR_ARRAY or Eina_List), maps
    (as Eina_Hash with string-convertible keys), and all scalar types. UnmarshalStruct
    performs the reverse conversion, with map key support for int/uint/float/string
    types. Both include comprehensive test coverage for round-trip serialization.
    
    Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
---
 eet/eet_test.go  |  34 +++++++
 eet/marshal.go   | 292 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 eet/unmarshal.go | 281 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 607 insertions(+)

diff --git a/eet/eet_test.go b/eet/eet_test.go
index d14c7b4..fd6c895 100644
--- a/eet/eet_test.go
+++ b/eet/eet_test.go
@@ -119,6 +119,40 @@ func TestDescriptorCached(t *testing.T) {
 	}
 }
 
+func TestMarshalUnmarshalRoundTrip(t *testing.T) {
+	type Basic struct {
+		I int     `eet:"i"`
+		F float64 `eet:"f"`
+		S string  `eet:"s"`
+		B bool    `eet:"b"`
+	}
+
+	original := Basic{I: 42, F: 3.14, S: "hello", B: true}
+	buf, cleanup, err := eet.MarshalStruct(reflect.ValueOf(original))
+	if err != nil {
+		t.Fatalf("MarshalStruct: %v", err)
+	}
+	defer cleanup()
+
+	var result Basic
+	if err := eet.UnmarshalStruct(buf, reflect.ValueOf(&result).Elem()); err != nil {
+		t.Fatalf("UnmarshalStruct: %v", err)
+	}
+
+	if result.I != 42 {
+		t.Errorf("I = %d, want 42", result.I)
+	}
+	if result.F != 3.14 {
+		t.Errorf("F = %f, want 3.14", result.F)
+	}
+	if result.S != "hello" {
+		t.Errorf("S = %q, want %q", result.S, "hello")
+	}
+	if result.B != true {
+		t.Errorf("B = %v, want true", result.B)
+	}
+}
+
 func TestParseTag(t *testing.T) {
 	tests := []struct {
 		tag        string
diff --git a/eet/marshal.go b/eet/marshal.go
new file mode 100644
index 0000000..514d023
--- /dev/null
+++ b/eet/marshal.go
@@ -0,0 +1,292 @@
+package eet
+
+/*
+#include <Eet.h>
+#include <Eina.h>
+#include <stdlib.h>
+
+static Eina_Bool _ego_hash_add(Eina_Hash *h, const char *k, void *d) {
+    return eina_hash_add(h, k, d);
+}
+*/
+import "C"
+import (
+	"fmt"
+	"reflect"
+	"unsafe"
+)
+
+// MarshalStruct encodes the Go struct v into a newly allocated C shadow buffer
+// whose layout matches the EET descriptor for v's type. The caller must invoke
+// the returned cleanup function to release all C memory once EET has consumed
+// the buffer.
+func MarshalStruct(v reflect.Value) (buf unsafe.Pointer, cleanup func(), err error) {
+	t := v.Type()
+	if t.Kind() == reflect.Pointer {
+		t = t.Elem()
+		v = v.Elem()
+	}
+
+	ti, err := GetTypeInfo(t)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	// Allocate a zeroed C buffer large enough for the entire shadow struct.
+	buf = C.calloc(1, C.size_t(ti.shadowSize))
+	if buf == nil {
+		return nil, nil, fmt.Errorf("%w: calloc failed for %s", ErrEncode, t.Name())
+	}
+
+	// Track all additional C allocations so cleanup can free them all.
+	var extras []unsafe.Pointer
+	addExtra := func(p unsafe.Pointer) { extras = append(extras, p) }
+
+	cleanup = func() {
+		for _, p := range extras {
+			C.free(p)
+		}
+		C.free(buf)
+	}
+
+	if err := marshalFields(v, buf, ti, addExtra); err != nil {
+		cleanup()
+		return nil, nil, err
+	}
+	return buf, cleanup, nil
+}
+
+// marshalFields writes each field of the Go struct v into the C buffer buf
+// according to the field descriptors in ti. addExtra registers any C
+// allocations that must be freed alongside buf.
+func marshalFields(v reflect.Value, buf unsafe.Pointer, ti *typeInfo, addExtra func(unsafe.Pointer)) error {
+	for _, fd := range ti.fields {
+		fv := v.Field(fd.GoIndex)
+		ptr := unsafe.Add(buf, fd.Offset)
+		if err := marshalField(fv, ptr, fd, addExtra); err != nil {
+			return fmt.Errorf("field %s: %w", fd.Name, err)
+		}
+	}
+	return nil
+}
+
+// marshalField writes a single Go field value into the C memory at ptr.
+func marshalField(fv reflect.Value, ptr unsafe.Pointer, fd fieldDescriptor, addExtra func(unsafe.Pointer)) error {
+	switch fv.Kind() {
+	case reflect.Bool:
+		var v C.uchar
+		if fv.Bool() {
+			v = 1
+		}
+		*(*C.uchar)(ptr) = v
+
+	case reflect.Int8:
+		*(*C.schar)(ptr) = C.schar(fv.Int())
+	case reflect.Int16:
+		*(*C.short)(ptr) = C.short(fv.Int())
+	case reflect.Int, reflect.Int32:
+		*(*C.int)(ptr) = C.int(fv.Int())
+	case reflect.Int64:
+		*(*C.longlong)(ptr) = C.longlong(fv.Int())
+
+	case reflect.Uint8:
+		*(*C.uchar)(ptr) = C.uchar(fv.Uint())
+	case reflect.Uint16:
+		*(*C.ushort)(ptr) = C.ushort(fv.Uint())
+	case reflect.Uint, reflect.Uint32:
+		*(*C.uint)(ptr) = C.uint(fv.Uint())
+	case reflect.Uint64:
+		*(*C.ulonglong)(ptr) = C.ulonglong(fv.Uint())
+
+	case reflect.Float32:
+		*(*C.float)(ptr) = C.float(fv.Float())
+	case reflect.Float64:
+		*(*C.double)(ptr) = C.double(fv.Float())
+
+	case reflect.String:
+		cs := C.CString(fv.String())
+		addExtra(unsafe.Pointer(cs))
+		*(*uintptr)(ptr) = uintptr(unsafe.Pointer(cs))
+
+	case reflect.Slice:
+		return marshalSlice(fv, ptr, fd, addExtra)
+
+	case reflect.Map:
+		return marshalMap(fv, ptr, fd, addExtra)
+
+	case reflect.Struct:
+		// Inline struct: marshal recursively at the same offset within buf.
+		if fd.subInfo == nil {
+			return fmt.Errorf("%w: no subInfo for struct field %s", ErrUnsupported, fd.Name)
+		}
+		return marshalFields(fv, ptr, fd.subInfo, addExtra)
+
+	case reflect.Pointer:
+		if fv.IsNil() {
+			// Leave the pointer slot as zero (calloc already zeroed it).
+			return nil
+		}
+		elem := fv.Elem()
+		if fd.subInfo == nil {
+			return fmt.Errorf("%w: no subInfo for pointer field %s", ErrUnsupported, fd.Name)
+		}
+		subBuf := C.calloc(1, C.size_t(fd.subInfo.shadowSize))
+		if subBuf == nil {
+			return fmt.Errorf("%w: calloc failed for pointer field %s", ErrEncode, fd.Name)
+		}
+		addExtra(subBuf)
+		if err := marshalFields(elem, subBuf, fd.subInfo, addExtra); err != nil {
+			return err
+		}
+		*(*uintptr)(ptr) = uintptr(subBuf)
+
+	default:
+		return fmt.Errorf("%w: kind %s", ErrUnsupported, fv.Kind())
+	}
+	return nil
+}
+
+// marshalSlice handles both EET_G_VAR_ARRAY and EET_G_LIST slice kinds.
+func marshalSlice(fv reflect.Value, ptr unsafe.Pointer, fd fieldDescriptor, addExtra func(unsafe.Pointer)) error {
+	n := fv.Len()
+	if fd.Tag.List {
+		// Build an Eina_List* from the slice elements.
+		var list *C.Eina_List
+		for i := 0; i < n; i++ {
+			elemPtr, err := marshalElem(fv.Index(i), fd, addExtra)
+			if err != nil {
+				return err
+			}
+			list = C.eina_list_append(list, elemPtr)
+		}
+		*(*uintptr)(ptr) = uintptr(unsafe.Pointer(list))
+		return nil
+	}
+
+	// VAR_ARRAY: contiguous C array of elements.
+	// Layout: [ptr: 8 bytes][count: 4 bytes][pad: 4 bytes] = 16 bytes total.
+	elemType := fv.Type().Elem()
+	elemSize, _ := cSizeAlign(elemType, FieldTag{})
+
+	var arrPtr unsafe.Pointer
+	if n > 0 {
+		arrPtr = C.calloc(C.size_t(n), C.size_t(elemSize))
+		if arrPtr == nil {
+			return fmt.Errorf("%w: calloc failed for slice field %s", ErrEncode, fd.Name)
+		}
+		addExtra(arrPtr)
+		for i := 0; i < n; i++ {
+			elemDst := unsafe.Add(arrPtr, i*elemSize)
+			if err := marshalScalarElem(fv.Index(i), elemDst, fd.subInfo, addExtra); err != nil {
+				return err
+			}
+		}
+	}
+
+	// Write pointer at offset, count (int32) at offset+8.
+	*(*uintptr)(ptr) = uintptr(arrPtr)
+	*(*C.int)(unsafe.Add(ptr, 8)) = C.int(n)
+	return nil
+}
+
+// marshalMap builds an Eina_Hash* from a Go map with string-convertible keys.
+func marshalMap(fv reflect.Value, ptr unsafe.Pointer, fd fieldDescriptor, addExtra func(unsafe.Pointer)) error {
+	if fv.IsNil() {
+		return nil
+	}
+
+	var hash *C.Eina_Hash
+
+	iter := fv.MapRange()
+	for iter.Next() {
+		keyStr := fmt.Sprint(iter.Key().Interface())
+		ck := C.CString(keyStr)
+		addExtra(unsafe.Pointer(ck))
+
+		valPtr, err := marshalElem(iter.Value(), fd, addExtra)
+		if err != nil {
+			return err
+		}
+		if hash == nil {
+			hash = C.eina_hash_string_superfast_new(nil)
+			addExtra(unsafe.Pointer(hash))
+		}
+		C._ego_hash_add(hash, ck, valPtr)
+	}
+
+	*(*uintptr)(ptr) = uintptr(unsafe.Pointer(hash))
+	return nil
+}
+
+// marshalElem marshals a single slice/map element and returns a C pointer to it.
+// For struct elements the pointer points to an allocated shadow buffer; for
+// scalar types the pointer is a newly allocated scalar-sized block.
+func marshalElem(ev reflect.Value, fd fieldDescriptor, addExtra func(unsafe.Pointer)) (unsafe.Pointer, error) {
+	if ev.Kind() == reflect.Struct && fd.subInfo != nil {
+		subBuf := C.calloc(1, C.size_t(fd.subInfo.shadowSize))
+		if subBuf == nil {
+			return nil, fmt.Errorf("%w: calloc for elem", ErrEncode)
+		}
+		addExtra(subBuf)
+		if err := marshalFields(ev, subBuf, fd.subInfo, addExtra); err != nil {
+			return nil, err
+		}
+		return subBuf, nil
+	}
+	// Scalar element: allocate a small block and write the value.
+	elemSize, _ := cSizeAlign(ev.Type(), FieldTag{})
+	p := C.calloc(1, C.size_t(elemSize))
+	if p == nil {
+		return nil, fmt.Errorf("%w: calloc for scalar elem", ErrEncode)
+	}
+	addExtra(p)
+	if err := marshalScalarElem(ev, p, fd.subInfo, addExtra); err != nil {
+		return nil, err
+	}
+	return p, nil
+}
+
+// marshalScalarElem writes a scalar (non-slice, non-map) element value into
+// the C memory at dst. It handles the same primitive types as marshalField.
+func marshalScalarElem(ev reflect.Value, dst unsafe.Pointer, sub *typeInfo, addExtra func(unsafe.Pointer)) error {
+	switch ev.Kind() {
+	case reflect.Bool:
+		var v C.uchar
+		if ev.Bool() {
+			v = 1
+		}
+		*(*C.uchar)(dst) = v
+	case reflect.Int8:
+		*(*C.schar)(dst) = C.schar(ev.Int())
+	case reflect.Int16:
+		*(*C.short)(dst) = C.short(ev.Int())
+	case reflect.Int, reflect.Int32:
+		*(*C.int)(dst) = C.int(ev.Int())
+	case reflect.Int64:
+		*(*C.longlong)(dst) = C.longlong(ev.Int())
+	case reflect.Uint8:
+		*(*C.uchar)(dst) = C.uchar(ev.Uint())
+	case reflect.Uint16:
+		*(*C.ushort)(dst) = C.ushort(ev.Uint())
+	case reflect.Uint, reflect.Uint32:
+		*(*C.uint)(dst) = C.uint(ev.Uint())
+	case reflect.Uint64:
+		*(*C.ulonglong)(dst) = C.ulonglong(ev.Uint())
+	case reflect.Float32:
+		*(*C.float)(dst) = C.float(ev.Float())
+	case reflect.Float64:
+		*(*C.double)(dst) = C.double(ev.Float())
+	case reflect.String:
+		cs := C.CString(ev.String())
+		addExtra(unsafe.Pointer(cs))
+		*(*uintptr)(dst) = uintptr(unsafe.Pointer(cs))
+	case reflect.Struct:
+		if sub == nil {
+			return fmt.Errorf("%w: no subInfo for struct elem", ErrUnsupported)
+		}
+		return marshalFields(ev, dst, sub, addExtra)
+	default:
+		return fmt.Errorf("%w: elem kind %s", ErrUnsupported, ev.Kind())
+	}
+	return nil
+}
diff --git a/eet/unmarshal.go b/eet/unmarshal.go
new file mode 100644
index 0000000..2cfafbd
--- /dev/null
+++ b/eet/unmarshal.go
@@ -0,0 +1,281 @@
+package eet
+
+/*
+#include <Eet.h>
+#include <Eina.h>
+#include <stdlib.h>
+*/
+import "C"
+import (
+	"fmt"
+	"reflect"
+	"unsafe"
+)
+
+// UnmarshalStruct decodes the C shadow buffer buf into the Go struct dst.
+// dst must be a settable reflect.Value of struct kind.
+func UnmarshalStruct(buf unsafe.Pointer, dst reflect.Value) error {
+	t := dst.Type()
+	if t.Kind() == reflect.Pointer {
+		t = t.Elem()
+		dst = dst.Elem()
+	}
+
+	ti, err := GetTypeInfo(t)
+	if err != nil {
+		return err
+	}
+	return unmarshalFields(buf, dst, ti)
+}
+
+// unmarshalFields reads each field from buf into the corresponding Go field of v.
+func unmarshalFields(buf unsafe.Pointer, v reflect.Value, ti *typeInfo) error {
+	for _, fd := range ti.fields {
+		fv := v.Field(fd.GoIndex)
+		ptr := unsafe.Add(buf, fd.Offset)
+		if err := unmarshalField(ptr, fv, fd); err != nil {
+			return fmt.Errorf("field %s: %w", fd.Name, err)
+		}
+	}
+	return nil
+}
+
+// unmarshalField reads a single C field at ptr and sets the Go value fv.
+func unmarshalField(ptr unsafe.Pointer, fv reflect.Value, fd fieldDescriptor) error {
+	switch fv.Kind() {
+	case reflect.Bool:
+		fv.SetBool(*(*C.uchar)(ptr) != 0)
+
+	case reflect.Int8:
+		fv.SetInt(int64(*(*C.schar)(ptr)))
+	case reflect.Int16:
+		fv.SetInt(int64(*(*C.short)(ptr)))
+	case reflect.Int, reflect.Int32:
+		fv.SetInt(int64(*(*C.int)(ptr)))
+	case reflect.Int64:
+		fv.SetInt(int64(*(*C.longlong)(ptr)))
+
+	case reflect.Uint8:
+		fv.SetUint(uint64(*(*C.uchar)(ptr)))
+	case reflect.Uint16:
+		fv.SetUint(uint64(*(*C.ushort)(ptr)))
+	case reflect.Uint, reflect.Uint32:
+		fv.SetUint(uint64(*(*C.uint)(ptr)))
+	case reflect.Uint64:
+		fv.SetUint(uint64(*(*C.ulonglong)(ptr)))
+
+	case reflect.Float32:
+		fv.SetFloat(float64(*(*C.float)(ptr)))
+	case reflect.Float64:
+		fv.SetFloat(float64(*(*C.double)(ptr)))
+
+	case reflect.String:
+		rawPtr := *(*uintptr)(ptr)
+		if rawPtr == 0 {
+			fv.SetString("")
+		} else {
+			fv.SetString(C.GoString((*C.char)(unsafe.Pointer(rawPtr))))
+		}
+
+	case reflect.Slice:
+		return unmarshalSlice(ptr, fv, fd)
+
+	case reflect.Map:
+		return unmarshalMap(ptr, fv, fd)
+
+	case reflect.Struct:
+		if fd.subInfo == nil {
+			return fmt.Errorf("%w: no subInfo for struct field %s", ErrUnsupported, fd.Name)
+		}
+		return unmarshalFields(ptr, fv, fd.subInfo)
+
+	case reflect.Pointer:
+		rawPtr := *(*uintptr)(ptr)
+		if rawPtr == 0 {
+			// Leave the Go pointer as nil.
+			return nil
+		}
+		if fd.subInfo == nil {
+			return fmt.Errorf("%w: no subInfo for pointer field %s", ErrUnsupported, fd.Name)
+		}
+		newVal := reflect.New(fv.Type().Elem())
+		if err := unmarshalFields(unsafe.Pointer(rawPtr), newVal.Elem(), fd.subInfo); err != nil {
+			return err
+		}
+		fv.Set(newVal)
+
+	default:
+		return fmt.Errorf("%w: kind %s", ErrUnsupported, fv.Kind())
+	}
+	return nil
+}
+
+// unmarshalSlice handles both EET_G_VAR_ARRAY and EET_G_LIST slice kinds.
+func unmarshalSlice(ptr unsafe.Pointer, fv reflect.Value, fd fieldDescriptor) error {
+	elemType := fv.Type().Elem()
+
+	if fd.Tag.List {
+		// Read an Eina_List* and walk its nodes.
+		list := (*C.Eina_List)(unsafe.Pointer(*(*uintptr)(ptr)))
+		var elems []reflect.Value
+		node := list
+		for node != nil {
+			data := C.eina_list_data_get(node)
+			ev := reflect.New(elemType).Elem()
+			if err := unmarshalElemPtr(unsafe.Pointer(data), ev, fd.subInfo); err != nil {
+				return err
+			}
+			elems = append(elems, ev)
+			node = (*C.Eina_List)(unsafe.Pointer(node.next))
+		}
+		sl := reflect.MakeSlice(fv.Type(), len(elems), len(elems))
+		for i, ev := range elems {
+			sl.Index(i).Set(ev)
+		}
+		fv.Set(sl)
+		return nil
+	}
+
+	// VAR_ARRAY: [ptr: 8 bytes][count: int32: 4 bytes][pad: 4 bytes]
+	arrPtr := *(*uintptr)(ptr)
+	count := int(*(*C.int)(unsafe.Add(ptr, 8)))
+
+	if count <= 0 || arrPtr == 0 {
+		fv.Set(reflect.MakeSlice(fv.Type(), 0, 0))
+		return nil
+	}
+
+	elemSize, _ := cSizeAlign(elemType, FieldTag{})
+	sl := reflect.MakeSlice(fv.Type(), count, count)
+	for i := 0; i < count; i++ {
+		elemPtr := unsafe.Add(unsafe.Pointer(arrPtr), i*elemSize)
+		ev := sl.Index(i)
+		if err := unmarshalScalarElem(elemPtr, ev, fd.subInfo); err != nil {
+			return err
+		}
+	}
+	fv.Set(sl)
+	return nil
+}
+
+// unmarshalMap iterates an Eina_Hash* and populates a Go map.
+func unmarshalMap(ptr unsafe.Pointer, fv reflect.Value, fd fieldDescriptor) error {
+	hash := (*C.Eina_Hash)(unsafe.Pointer(*(*uintptr)(ptr)))
+	if hash == nil {
+		return nil
+	}
+
+	mapType := fv.Type()
+	keyType := mapType.Key()
+	valType := mapType.Elem()
+
+	m := reflect.MakeMap(mapType)
+
+	it := C.eina_hash_iterator_tuple_new(hash)
+	if it == nil {
+		fv.Set(m)
+		return nil
+	}
+	defer C.eina_iterator_free(it)
+
+	var data unsafe.Pointer
+	for C.eina_iterator_next(it, (*unsafe.Pointer)(unsafe.Pointer(&data))) != 0 {
+		tuple := (*C.Eina_Hash_Tuple)(data)
+
+		keyStr := C.GoString((*C.char)(tuple.key))
+		kv, err := convertStringToKey(keyStr, keyType)
+		if err != nil {
+			return err
+		}
+
+		vv := reflect.New(valType).Elem()
+		if err := unmarshalElemPtr(tuple.data, vv, fd.subInfo); err != nil {
+			return err
+		}
+		m.SetMapIndex(kv, vv)
+	}
+
+	fv.Set(m)
+	return nil
+}
+
+// unmarshalElemPtr reads a C element from the given data pointer into ev.
+// For struct types data points to a full shadow buffer; for scalars it points
+// to the value directly.
+func unmarshalElemPtr(data unsafe.Pointer, ev reflect.Value, sub *typeInfo) error {
+	if ev.Kind() == reflect.Struct && sub != nil {
+		return unmarshalFields(data, ev, sub)
+	}
+	return unmarshalScalarElem(data, ev, sub)
+}
+
+// unmarshalScalarElem reads a scalar value from the C memory at src into ev.
+func unmarshalScalarElem(src unsafe.Pointer, ev reflect.Value, sub *typeInfo) error {
+	switch ev.Kind() {
+	case reflect.Bool:
+		ev.SetBool(*(*C.uchar)(src) != 0)
+	case reflect.Int8:
+		ev.SetInt(int64(*(*C.schar)(src)))
+	case reflect.Int16:
+		ev.SetInt(int64(*(*C.short)(src)))
+	case reflect.Int, reflect.Int32:
+		ev.SetInt(int64(*(*C.int)(src)))
+	case reflect.Int64:
+		ev.SetInt(int64(*(*C.longlong)(src)))
+	case reflect.Uint8:
+		ev.SetUint(uint64(*(*C.uchar)(src)))
+	case reflect.Uint16:
+		ev.SetUint(uint64(*(*C.ushort)(src)))
+	case reflect.Uint, reflect.Uint32:
+		ev.SetUint(uint64(*(*C.uint)(src)))
+	case reflect.Uint64:
+		ev.SetUint(uint64(*(*C.ulonglong)(src)))
+	case reflect.Float32:
+		ev.SetFloat(float64(*(*C.float)(src)))
+	case reflect.Float64:
+		ev.SetFloat(float64(*(*C.double)(src)))
+	case reflect.String:
+		rawPtr := *(*uintptr)(src)
+		if rawPtr == 0 {
+			ev.SetString("")
+		} else {
+			ev.SetString(C.GoString((*C.char)(unsafe.Pointer(rawPtr))))
+		}
+	case reflect.Struct:
+		if sub == nil {
+			return fmt.Errorf("%w: no subInfo for struct elem", ErrUnsupported)
+		}
+		return unmarshalFields(src, ev, sub)
+	default:
+		return fmt.Errorf("%w: elem kind %s", ErrUnsupported, ev.Kind())
+	}
+	return nil
+}
+
+// convertStringToKey converts an EET string key back to the Go map key type.
+func convertStringToKey(s string, t reflect.Type) (reflect.Value, error) {
+	switch t.Kind() {
+	case reflect.String:
+		return reflect.ValueOf(s).Convert(t), nil
+	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+		var n int64
+		if _, err := fmt.Sscan(s, &n); err != nil {
+			return reflect.Value{}, fmt.Errorf("%w: cannot parse map key %q as %s: %v", ErrDecode, s, t, err)
+		}
+		return reflect.ValueOf(n).Convert(t), nil
+	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+		var n uint64
+		if _, err := fmt.Sscan(s, &n); err != nil {
+			return reflect.Value{}, fmt.Errorf("%w: cannot parse map key %q as %s: %v", ErrDecode, s, t, err)
+		}
+		return reflect.ValueOf(n).Convert(t), nil
+	case reflect.Float32, reflect.Float64:
+		var f float64
+		if _, err := fmt.Sscan(s, &f); err != nil {
+			return reflect.Value{}, fmt.Errorf("%w: cannot parse map key %q as %s: %v", ErrDecode, s, t, err)
+		}
+		return reflect.ValueOf(f).Convert(t), nil
+	default:
+		return reflect.Value{}, fmt.Errorf("%w: unsupported map key type %s", ErrUnsupported, t)
+	}
+}

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.

Reply via email to