-
Notifications
You must be signed in to change notification settings - Fork 2
/
i2s.go
131 lines (111 loc) · 2.77 KB
/
i2s.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
package main
import (
"fmt"
"reflect"
)
func i2s(data interface{}, out interface{}) error {
outVal := reflect.ValueOf(out)
if outVal.Kind() != reflect.Ptr {
return fmt.Errorf("out argument type must be a pointer")
}
outVal = outVal.Elem()
if !outVal.CanSet() {
return fmt.Errorf("out argument must be set")
}
switch in := data.(type) {
case map[string]interface{}:
return parseStruct(in, outVal)
case []interface{}:
return parseSlice(in, outVal)
default:
return fmt.Errorf("unknown data argument type %T", data)
}
}
func parseStruct(in map[string]interface{}, outVal reflect.Value) error {
if outVal.Kind() != reflect.Struct {
return fmt.Errorf("out argument type must be a struct")
}
outType := outVal.Type()
for i := 0; i < outVal.NumField(); i++ {
fieldVal := outVal.Field(i)
if !fieldVal.CanSet() {
continue
}
fieldType := fieldVal.Type()
fieldName := outType.Field(i).Name
inVal, exist := in[fieldName]
if !exist {
continue
}
switch fieldType.Kind() {
case reflect.Int:
var intVal int64
switch val := inVal.(type) {
case int:
intVal = int64(val)
case float64:
intVal = int64(val)
default:
return fmt.Errorf(
"unsupport type %T to assign into the struct field %q (type=%q)",
inVal, fieldName, fieldType,
)
}
fieldVal.SetInt(intVal)
case reflect.String:
strVal, ok := inVal.(string)
if !ok {
return fmt.Errorf(
"unsupport type %T to assign into the struct field %q (type=%q)",
inVal, fieldName, fieldType,
)
}
fieldVal.SetString(strVal)
case reflect.Bool:
boolVal, ok := inVal.(bool)
if !ok {
return fmt.Errorf(
"unsupport type %T to assign into the struct field %q (type=%q)",
inVal, fieldName, fieldType,
)
}
fieldVal.SetBool(boolVal)
case reflect.Struct:
m, ok := inVal.(map[string]interface{})
if !ok {
return fmt.Errorf("failed to assert type %T to map", inVal)
}
if err := parseStruct(m, fieldVal); err != nil {
return err
}
case reflect.Slice:
sl, ok := inVal.([]interface{})
if !ok {
return fmt.Errorf("failed to assert type %T to slice", inVal)
}
if err := parseSlice(sl, fieldVal); err != nil {
return err
}
default:
return fmt.Errorf("unsupport type %q (fieldName=%q)", fieldType, fieldName)
}
}
return nil
}
func parseSlice(sl []interface{}, outVal reflect.Value) error {
if outVal.Kind() != reflect.Slice {
return fmt.Errorf("out argument type must be a slice")
}
for _, elem := range sl {
m, ok := elem.(map[string]interface{})
if !ok {
return fmt.Errorf("failed to assert type %T to map", elem)
}
v := reflect.New(outVal.Type().Elem()).Elem()
if err := parseStruct(m, v); err != nil {
return err
}
outVal.Set(reflect.Append(outVal, v))
}
return nil
}