-
Notifications
You must be signed in to change notification settings - Fork 0
/
message.go
135 lines (124 loc) · 3.1 KB
/
message.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
package gometeor
import (
"bytes"
"encoding/json"
"errors"
"reflect"
"strings"
)
type RawMessage map[string]json.RawMessage
func _removeTrailingNewline(buf *bytes.Buffer) {
l := buf.Len()
if l > 0 && buf.Bytes()[l-1] == '\n' {
buf.Truncate(l - 1)
}
}
// ToJSON encodes a struct or map to JSON as the meteor protocol wants it.
func ToJSON(obj interface{}) ([]byte, error) {
value := reflect.ValueOf(obj)
for value.Kind() == reflect.Ptr {
value = value.Elem()
}
bufw := bytes.NewBuffer(nil)
if value.Kind() == reflect.Struct {
typ := value.Type()
nfield := value.NumField()
bufw.WriteString("{")
for i := 0; i < nfield; i++ {
ftyp := typ.Field(i)
f := value.Field(i)
// TODO: json tags.
if i != 0 {
bufw.WriteString(",")
}
name := strings.ToLower(ftyp.Name)
err := json.NewEncoder(bufw).Encode(name)
_removeTrailingNewline(bufw)
if err != nil {
return nil, err
}
bufw.WriteString(":")
err = json.NewEncoder(bufw).Encode(f.Interface())
_removeTrailingNewline(bufw)
if err != nil {
return nil, err
}
}
bufw.WriteString("}")
}
return bufw.Bytes(), nil
}
func (m RawMessage) Decode(obj interface{}) error {
// obj had better be a pointer to a structure.
value := reflect.ValueOf(obj)
if value.Kind() != reflect.Ptr {
return errors.New("Invalid receiver")
}
if value.IsNil() {
return errors.New("Invalid receiver")
}
elem := value.Elem()
if elem.Kind() != reflect.Struct {
return errors.New("Invalid receiver")
}
// For each field in the struct
typ := elem.Type()
nfield := elem.NumField()
for i := 0; i < nfield; i++ {
ftyp := typ.Field(i)
f := elem.Field(i)
// TODO: json tags.
name := ftyp.Name
raw, ok := m[name]
if !ok {
raw, ok = m[strings.ToLower(name)]
}
if ok {
err := json.Unmarshal(raw, f.Addr().Interface())
if err != nil {
// TODO: um, something else.
return err
}
}
}
return nil
}
// OK, maybe this belongs in its own file.
// Call the given function with the given arguments.
// fn must be a function that returns a single result,
// and args must be unmarshallable into the argument types of the functions.
func Call(fn interface{}, args []json.RawMessage) (result interface{}, err error) {
// It's awfully easy to panic in these situations
defer func() {
if e := recover(); e != nil {
err = errors.New("Error in call")
}
}()
fnvalue := reflect.ValueOf(fn)
if fnvalue.Kind() != reflect.Func {
return nil, errors.New("Non-function passed to call")
}
fntyp := fnvalue.Type()
nin := fntyp.NumIn()
argvals := make([]reflect.Value, nin)
if nin != len(args) {
return nil, errors.New("Wrong number of arguments passed to call")
}
if fntyp.NumOut() > 1 {
return nil, errors.New("Function must return at most one result")
}
for i := 0; i < nin; i++ {
argtyp := fntyp.In(i)
argval := reflect.New(argtyp)
err := json.Unmarshal(args[i], argval.Interface())
if err != nil {
return nil, errors.New("Could not unmarshall parameters")
}
argvals[i] = argval.Elem()
}
results := fnvalue.Call(argvals)
if len(results) > 0 {
return results[0].Interface(), nil
}
return nil, nil
}