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.