-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathto_go.go
241 lines (229 loc) Β· 6.33 KB
/
to_go.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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
package godence
import (
"fmt"
"math/big"
"reflect"
"github.com/onflow/cadence"
)
// getFieldByName. find cadence field by go field name.
func getFieldByName(name string, value cadence.Value) (cadence.Value, error) {
switch v := value.(type) {
case cadence.Struct:
for index, field := range v.StructType.Fields {
if name == field.Identifier {
return v.Fields[index], nil
}
}
case cadence.Event:
for index, field := range v.EventType.Fields {
if name == field.Identifier {
return v.Fields[index], nil
}
}
case cadence.Resource:
for index, field := range v.ResourceType.Fields {
if name == field.Identifier {
return v.Fields[index], nil
}
}
}
// not found, return void and error
return cadence.NewVoid(), fmt.Errorf("cannot find field named %s in cadence struct/event/resource", name)
}
// structEventResourceToGoStruct
func structEventResourceToGoStruct(value cadence.Value, dist any) (err error) {
distT := reflect.TypeOf(dist)
distV := reflect.ValueOf(dist)
defer func() {
if msg := recover(); msg != nil {
err = fmt.Errorf("structEventResourceToGoStruct, panic recoverd: %v", msg)
}
}()
// traverse all dist fields.
for fieldIndex := 0; fieldIndex < distT.Elem().NumField(); fieldIndex++ {
fieldT := distT.Elem().Field(fieldIndex)
fieldV := distV.Elem().Field(fieldIndex)
// cannot set, skip
if !fieldV.CanSet() {
continue
}
// find cadence field by go field
fieldName := fieldT.Name
if tagValue, ok := fieldT.Tag.Lookup("godence"); ok {
// get cadence field name specified by tag
fieldName = tagValue
}
// if error
if v, err := getFieldByName(fieldName, value); err == nil {
if fieldV.Kind() == reflect.Pointer {
// if big int
if fieldT.Type.Elem().PkgPath() == "math/big" && fieldT.Type.Elem().Name() == "Int" {
fieldV.Set(reflect.ValueOf(v.ToGoValue()))
} else {
// embedded struct
// should use type to get kind
if fieldT.Type.Elem().Kind() == reflect.Struct {
// check nil
if fieldV.IsNil() {
// new a value
fieldV.Set(reflect.New(fieldT.Type.Elem()))
}
toGoStruct(v, fieldV.Interface())
}
}
} else {
toGoReflect(v, &fieldV)
}
} else if err != nil {
return err
}
}
return
}
// toGoReflect. the same as ToGo, but receive reflect.Value
func toGoReflect(value cadence.Value, dist *reflect.Value) error {
switch dist.Type().String() {
default:
dist.Set(reflect.ValueOf(value.ToGoValue()))
return nil
// other
case "string": // Cadence String, Address, Path, Character(why?)
switch cv := value.(type) {
case cadence.Address, cadence.Path:
dist.Set(reflect.ValueOf(cv.String()))
return nil
}
dist.Set(reflect.ValueOf(value.ToGoValue()))
return nil
}
}
// toGoMap. call this function if type of dist is map kind.
func toGoMap(value cadence.Value, dist any) (err error) {
defer func() {
if e := recover(); e != nil {
err = fmt.Errorf("toGoMap, panic recoverd: %v", e)
}
}()
dic := value.(cadence.Dictionary)
distV := reflect.ValueOf(dist)
for _, retEntry := range dic.Pairs {
distV.SetMapIndex(reflect.ValueOf(retEntry.Key.ToGoValue()), reflect.ValueOf(retEntry.Value.ToGoValue()))
}
return
}
// toGoSlice. call this function if type of dist is array kind.
func toGoSlice(value cadence.Value, dist any) (err error) {
defer func() {
if e := recover(); e != nil {
err = fmt.Errorf("toGoSlice, panic recoverd: %v", e)
}
}()
dic := value.(cadence.Array)
distV := reflect.ValueOf(dist).Elem()
for _, retElment := range dic.Values {
distV.Set(reflect.Append(distV, reflect.ValueOf(retElment.ToGoValue())))
}
return
}
// toGoStruct. call this function if type of dist is struct kind.
func toGoStruct(value cadence.Value, dist any) error {
switch v := value.(type) {
case cadence.Optional:
return toGoStruct(v.Value, dist)
case cadence.Struct:
return structEventResourceToGoStruct(v, dist)
case cadence.Event:
return structEventResourceToGoStruct(v, dist)
case cadence.Resource:
return structEventResourceToGoStruct(v, dist)
}
return fmt.Errorf("to go struct: unsupport cadence type: %s", reflect.TypeOf(value))
}
func isValueAddressOrPath(value cadence.Value) bool {
switch value.(type) {
case cadence.Address, cadence.Path:
return true
}
return false
}
// ToGo. Convert cadence types to go.
// Param 1: cadence value to convert.
// Param 2: go pointer.
// Address convert to string, will have 0x prefix.
func ToGo(value cadence.Value, dist any) (err error) {
// type cast may be failed, should recover panic
defer func() {
if rec := recover(); rec != nil {
err = fmt.Errorf("panic recovered: %v", rec)
}
// defer function has no return expression.
// should use named return value.
}()
goTypeV := value.ToGoValue()
// check if optional is nil
if !isValueAddressOrPath(value) && goTypeV == nil {
reflect.ValueOf(dist).Elem().Set(reflect.Zero(reflect.TypeOf(dist).Elem()))
return
}
switch v := dist.(type) {
// integers
case **big.Int: // Cadence Int, Int128, Int256, UInt, UInt128, UInt256
*v = value.ToGoValue().(*big.Int)
return nil
case *int8:
*v = value.ToGoValue().(int8)
return nil
case *int16:
*v = value.ToGoValue().(int16)
return nil
case *int32:
*v = value.ToGoValue().(int32)
return nil
case *int64: // Cadence Int64, Fix64
*v = value.ToGoValue().(int64)
return nil
// unsigned integers
case *uint8:
*v = value.ToGoValue().(uint8)
return nil
case *uint16:
*v = value.ToGoValue().(uint16)
return nil
case *uint32:
*v = value.ToGoValue().(uint32)
return nil
case *uint64: // Cadence UInt64, UFix64
*v = value.ToGoValue().(uint64)
return nil
// other
case *string: // Cadence String, Address, Path, Character(why?)
if isValueAddressOrPath(value) {
*v = value.String()
return
}
*v = value.ToGoValue().(string)
return nil
case *[8]uint8: // Address
*v = value.ToGoValue().([8]uint8)
return nil
case *cadence.Address: // Address
*v = value.(cadence.Address)
return nil
case *bool:
*v = value.ToGoValue().(bool)
return nil
}
switch reflect.TypeOf(dist).Kind() {
// try to convert to struct type
case reflect.Pointer:
switch reflect.TypeOf(dist).Elem().Kind() {
case reflect.Struct:
return toGoStruct(value, dist)
case reflect.Slice:
return toGoSlice(value, dist)
}
case reflect.Map:
return toGoMap(value, dist)
}
return fmt.Errorf("unsupport type: %s", reflect.TypeOf(dist))
}