-
Notifications
You must be signed in to change notification settings - Fork 23
/
convert.go
147 lines (126 loc) · 4.67 KB
/
convert.go
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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
package runtime
import (
"fmt"
"reflect"
"github.com/mandelsoft/goutils/errors"
"github.com/mandelsoft/goutils/generics"
)
type Converter[T VersionedTypedObject, V TypedObject] interface {
// ConvertFrom converts from an internal version into an external format.
ConvertFrom(object T) (V, error)
// ConvertTo converts from an external format into an internal version.
ConvertTo(object V) (T, error)
}
type IdentityConverter[T TypedObject] struct{}
var _ Converter[VersionedTypedObject, VersionedTypedObject] = (*IdentityConverter[VersionedTypedObject])(nil)
func (_ IdentityConverter[T]) ConvertFrom(object T) (T, error) {
return object, nil
}
func (_ IdentityConverter[T]) ConvertTo(object T) (T, error) {
return object, nil
}
////////////////////////////////////////////////////////////////////////////////
type (
FormatVersion[T VersionedTypedObject] interface {
TypedObjectDecoder[T]
TypedObjectEncoder[T]
}
// _FormatVersion[T VersionedTypedObject] = FormatVersion[T] // I like Go.
_FormatVersion[T VersionedTypedObject] interface {
FormatVersion[T]
}
)
type formatVersion[T VersionedTypedObject, I VersionedTypedObject, V TypedObject] struct {
decoder TypedObjectDecoder[V]
converter Converter[I, V]
}
func (c *formatVersion[T, I, V]) Encode(object T, marshaler Marshaler) ([]byte, error) {
i, err := generics.TryCastE[I](object)
if err != nil {
return nil, err
}
v, err := c.converter.ConvertFrom(i)
if err != nil {
return nil, err
}
if marshaler == nil {
marshaler = DefaultJSONEncoding
}
return marshaler.Marshal(v)
}
func (c *formatVersion[T, I, V]) Decode(data []byte, unmarshaler Unmarshaler) (T, error) {
var _nil T
v, err := c.decoder.Decode(data, unmarshaler)
if err != nil {
return _nil, err
}
i, err := c.converter.ConvertTo(v)
if err != nil {
return _nil, err
}
return generics.TryCastE[T](i)
}
// caster applies an implementation to interface upcast for a format version,
// here it has to be a subtype of T, but thanks to Go this cannot be expressed.
type caster[T VersionedTypedObject, I VersionedTypedObject] struct {
version FormatVersion[I]
}
func (c *caster[T, I]) Decode(data []byte, unmarshaler Unmarshaler) (T, error) {
var _nil T
o, err := c.version.Decode(data, unmarshaler)
if err != nil {
return _nil, err
}
var i interface{} = o // type parameter based casts not supported by go
if t, ok := i.(T); ok {
return t, nil
}
return _nil, errors.ErrInvalid("type", fmt.Sprintf("%T", o))
}
func (c *caster[T, I]) Encode(o T, marshaler Marshaler) ([]byte, error) {
var t interface{} = o // type parameter based casts not supported by go
if i, ok := t.(I); ok {
return c.version.Encode(i, marshaler)
}
return nil, errors.ErrInvalid("type", fmt.Sprintf("%T", o))
}
type implementation struct {
VersionedTypedObject
}
var _ FormatVersion[VersionedTypedObject] = (*caster[VersionedTypedObject, implementation])(nil)
// NewSimpleVersion creates a new format version for versioned typed objects,
// where T is the common *interface* of all types of the same type realm.
// It creates an external version identical to the internal representation (type I).
// This must be a struct pointer type.
func NewSimpleVersion[T VersionedTypedObject, I VersionedTypedObject]() FormatVersion[T] {
var proto I // first time use of typed structure nil pointers
_, err := generics.TryCastE[T](proto)
if err != nil {
var t *T
panic(fmt.Errorf("invalid type %T: does not implement required interface %s", proto, reflect.TypeOf(t).Elem()))
}
return &formatVersion[T, I, I]{
decoder: MustNewDirectDecoder[I](proto),
converter: &IdentityConverter[I]{},
}
}
// NewConvertedVersion creates a new format version for versioned typed objects,
// where T is the common *interface* of all types of the same type realm and I is the
// *internal implementation* commonly used for the various version variants of a dedicated kind of type,
// representing the format this format version is responsible for.
// Therefore, I must be subtype of T, which cannot be expressed in Go.
// The converter must convert between the external version, specified by the given prototype and
// the *internal* representation (type I) used to internally represent a set of variants as Go object.
func NewConvertedVersion[T VersionedTypedObject, I VersionedTypedObject, V TypedObject](converter Converter[I, V]) FormatVersion[T] {
var proto V
return &formatVersion[T, I, V]{
decoder: MustNewDirectDecoder[V](proto),
converter: converter,
}
}
func NewConvertedVersionByProto[T VersionedTypedObject, I VersionedTypedObject, V TypedObject](proto V, converter Converter[I, V]) FormatVersion[T] {
return &formatVersion[T, I, V]{
decoder: MustNewDirectDecoder[V](proto),
converter: converter,
}
}