-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathserialize.go
More file actions
98 lines (82 loc) · 3.15 KB
/
Copy pathserialize.go
File metadata and controls
98 lines (82 loc) · 3.15 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
package typemux
import (
"errors"
"fmt"
"reflect"
)
// ErrSerializerNotFound is returned when no serializer is registered for the value's
// type (regardless of DATA type).
var ErrSerializerNotFound = errors.New("serializer not found")
// ErrKeyTypeMismatch is returned when Serialize is invoked with a KEY type parameter
// that does not match the key type used at registration time.
var ErrKeyTypeMismatch = errors.New("key type mismatch")
// ErrDataTypeMismatch is returned when serializers are registered for the value's
// type, but none of them produce the DATA type requested by Serialize.
var ErrDataTypeMismatch = errors.New("data type mismatch")
type serializerFuncAny func(v any) (any, error)
type serializerEntry struct {
key any
fn serializerFuncAny
}
type serializerResolver interface {
getSerializer(typ, dataType reflect.Type) (serializerEntry, bool)
typeRegistered(typ reflect.Type) bool
}
// Serialize looks up the codec's marshal half for v's concrete type and the
// requested DATA, then produces the registered key and the marshaled data.
//
// DATA must be specified at the call site — a single registry may hold
// codecs producing different DATA types for the same value type, and the
// caller picks which one is expected. This mirrors CreateType[KEY, DATA].
//
// name, data, err := typemux.Marshal[string, []byte](sealed, value)
//
// If v is a pointer to a type registered by value, Serialize dereferences and
// serializes the underlying value.
//
// Returns:
// - ErrSerializerNotFound if no codec is registered for the value's type
// - ErrDataTypeMismatch if the type is registered but not for the requested DATA
// - ErrKeyTypeMismatch if the requested KEY type doesn't match the registered key
// - ErrUnsupported if the codec's marshal half was Unsupported
func Serialize[KEY comparable, DATA any](reg serializerResolver, v any) (KEY, DATA, error) {
var zeroK KEY
var zeroD DATA
typ := reflect.TypeOf(v)
if typ == nil {
return zeroK, zeroD, fmt.Errorf("typemux: %w for nil value", ErrSerializerNotFound)
}
dataType := reflect.TypeOf((*DATA)(nil)).Elem()
entry, ok := reg.getSerializer(typ, dataType)
if !ok && typ.Kind() == reflect.Ptr {
if e, ok2 := reg.getSerializer(typ.Elem(), dataType); ok2 {
entry = e
v = reflect.ValueOf(v).Elem().Interface()
ok = true
}
}
if !ok {
// Differentiate "type unknown" from "type known but DATA mismatch".
probe := typ
if typ.Kind() == reflect.Ptr && reg.typeRegistered(typ.Elem()) {
probe = typ.Elem()
}
if reg.typeRegistered(probe) {
return zeroK, zeroD, fmt.Errorf("typemux: %w: type %v has no serializer producing %v", ErrDataTypeMismatch, probe, dataType)
}
return zeroK, zeroD, fmt.Errorf("typemux: %w for type %v", ErrSerializerNotFound, typ)
}
k, ok := entry.key.(KEY)
if !ok {
return zeroK, zeroD, fmt.Errorf("typemux: %w: registered key is %T, requested %T", ErrKeyTypeMismatch, entry.key, zeroK)
}
result, err := entry.fn(v)
if err != nil {
return zeroK, zeroD, err
}
data, ok := result.(DATA)
if !ok {
return zeroK, zeroD, fmt.Errorf("typemux: %w: registered data is %T, requested %T", ErrDataTypeMismatch, result, zeroD)
}
return k, data, nil
}