-
Notifications
You must be signed in to change notification settings - Fork 4
/
aux.go
299 lines (257 loc) · 8.59 KB
/
aux.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
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
package api
import (
"encoding/json"
"fmt"
"log"
"net/http"
"reflect"
"strings"
)
var httpMethods = [...]string{
"GET",
"PUT",
"POST",
"DELETE",
"HEAD",
}
// Constants to test type equality
var (
responseWriterPtrType = reflect.TypeOf((*http.ResponseWriter)(nil))
responseWriterType = responseWriterPtrType.Elem()
requestPtrType = reflect.TypeOf((*http.Request)(nil))
requestType = requestPtrType.Elem()
errorSliceType = reflect.TypeOf(([]error)(nil))
errorType = errorSliceType.Elem()
errorNilValue = reflect.New(errorType).Elem()
)
// This method return true if the received type is an context type
// It means that it doesn't need to be mapped and will be present in the context
// It also return an error message if user used *http.ResponseWriter or used http.Request
// Context types include error and []error types
func isContextType(resourceType reflect.Type) bool {
// Test if user used *http.ResponseWriter insted of http.ResponseWriter
if resourceType.AssignableTo(responseWriterPtrType) {
log.Fatalf("You asked for %s when you should used %s", resourceType, responseWriterType)
}
// Test if user used http.Request insted of *http.Request
if resourceType.AssignableTo(requestType) {
log.Fatalf("You asked for %s when you should used %s", resourceType, requestPtrType)
}
return resourceType.AssignableTo(responseWriterType) ||
resourceType.AssignableTo(requestPtrType) ||
resourceType.AssignableTo(errorType) ||
resourceType.AssignableTo(errorSliceType) ||
resourceType.Implements(idInterfaceType)
}
// Return one Ptr to the given Value...
// - If receive Struct or *Struct, resturn *Struct
// - If receive []Struct, return *[]Struct
// - If receive []*Struct, return *[]*Struct
func ptrOfValue(value reflect.Value) reflect.Value {
ptr := reflect.New(elemOfType(value.Type()))
if value.Kind() == reflect.Ptr && !value.IsNil() {
ptr.Elem().Set(value.Elem())
}
if value.Kind() == reflect.Struct || value.Kind() == reflect.Slice {
ptr.Elem().Set(value)
}
return ptr
}
// Return the Value of the Ptr to Elem of the inside the given Slice
// []struct, *[]struct, *[]*struct -> return struct Value
func elemOfSliceValue(value reflect.Value) reflect.Value {
// If Value is a Ptr, get the Elem it points to
sliceValue := elemOfValue(value)
// Type of the Elem inside the given Slice
// []struct or []*struct -> return struct type
elemType := elemOfType(sliceValue.Type().Elem())
// Creates a new Ptr to Elem of the Type this Slice stores
ptrToElem := reflect.New(elemType)
if sliceValue.IsNil() || sliceValue.Len() == 0 {
// If given Slice is null or it has nothing inside it
// return the new empty Value of the Elem inside this slice
return ptrToElem
}
// If this slice has an Elem inside it
// and set the value of the first Elem inside this Slice
ptrToElem.Elem().Set(elemOfValue(sliceValue.Index(0)))
return ptrToElem
}
// If Value is a Ptr, return the Elem it points to
func elemOfValue(value reflect.Value) reflect.Value {
if value.Kind() == reflect.Ptr {
// It occours if an Struct has an Field that points itself
// so it should never occours, and will be cauch by the check for Circular Dependency
if !value.Elem().IsValid() {
value = reflect.New(elemOfType(value.Type()))
}
return value.Elem()
}
return value
}
// If Type is a Ptr, return the Type of the Elem it points to
func elemOfType(t reflect.Type) reflect.Type {
if t.Kind() == reflect.Ptr {
return t.Elem()
}
return t
}
// If Type is a Slice, return the Type of the Elem it stores
func elemOfSliceType(t reflect.Type) reflect.Type {
if t.Kind() == reflect.Slice {
return t.Elem()
}
return t
}
// If Type is a Ptr, return the Type of the Elem it points to
// If Type is a Slice, return the Type of the Elem it stores
func mainElemOfType(t reflect.Type) reflect.Type {
t = elemOfType(t)
t = elemOfSliceType(t)
t = elemOfType(t)
return t
}
// If Type is not a Ptr, return the one Type that points to it
func ptrOfType(t reflect.Type) reflect.Type {
if t.Kind() != reflect.Ptr {
return reflect.PtrTo(t)
}
return t
}
// Return the true if is an exported field of those of types
// Struct, *Struct, []Struct or []*Struct
// Check if this field is exported, begin with UpperCase: v.CanInterface()
// and if this field is valid fo create Resources: Structs or Slices of Structs
func isValidValue(v reflect.Value) bool {
if !v.CanInterface() {
return false
}
if mainElemOfType(v.Type()).Kind() == reflect.Struct {
return true
}
return false
}
// Return the true if the dependency is one of those types
// Interface, Struct, *Struct, []Struct or []*Struct
func isValidDependencyType(t reflect.Type) error {
t = elemOfType(t)
if t.Kind() == reflect.Interface || t.Kind() == reflect.Struct ||
t.Kind() == reflect.Slice && t.Elem().Kind() == reflect.Struct {
return nil
}
return fmt.Errorf("Type %s is not allowed as dependency", t)
}
// Return true if this Type is a Slice or Ptr to Slice
func isSliceType(t reflect.Type) bool {
if t.Kind() == reflect.Ptr {
t = t.Elem()
}
return t.Kind() == reflect.Slice
}
// 'Init' methods should have no Inputs,
// and it can just output an error
func isValidInit(m reflect.Method) error {
if m.Type.NumIn() > 1 {
return fmt.Errorf("Resource %s has an invalid Init method %s. "+
" It should have no inputs\n",
m.Type.In(0), m.Type)
}
if m.Type.NumOut() > 2 {
return fmt.Errorf("Resource %s has an invalid Init method %s. "+
" It can outputs just itself and one error\n",
m.Type.In(0), m.Type)
}
// Method Struct owner Type
owner := elemOfType(m.Type.In(0))
for i := 0; i < m.Type.NumOut(); i++ {
t := elemOfType(m.Type.Out(i))
// Ignore the Resource itself and the error types
if elemOfType(t) == owner || t == errorType {
continue
}
return fmt.Errorf("Resource %s has an invalid New method %s. "+
"It can't outputs %s\n", m.Type.In(0), m.Type, t)
}
return nil
}
// 'New' methods should have no Output,
// it should alter the first argument as a pointer
// Or, at least, return itself
func isValidConstructor(method reflect.Method) error {
// Test if New method return itself and/or error types
// just one of each type is accepted
itself := false
err := false
//errorType := reflect.TypeOf(errors.New("432"))
// Method Struct owner Type
owner := mainElemOfType(method.Type.In(0))
for i := 0; i < method.Type.NumOut(); i++ {
t := mainElemOfType(method.Type.Out(i))
if t == owner && !itself {
itself = true
continue
}
if t == errorType && !err {
err = true
continue
}
return fmt.Errorf("Resource %s has an invalid New method %s. "+
"It can't outputs %s \n", method.Type.In(0), method.Type, t)
}
return nil
}
// Return true if given StructField is an exported Field
// return false if is an unexported Field
func isExportedField(field reflect.StructField) bool {
firstChar := string([]rune(field.Name)[0])
return firstChar == strings.ToUpper(firstChar)
}
// Return a new empty Value for one of these Types
// Struct, *Struct, Slice, *Slice
func newEmptyValue(t reflect.Type) (reflect.Value, error) {
t = elemOfType(t)
if t.Kind() == reflect.Struct || t.Kind() == reflect.Slice {
return reflect.New(t), nil // A new Ptr to Struct of this type
}
return reflect.Value{}, fmt.Errorf("Can't create an empty Value for type %s", t)
}
// Return if this method should be mapped or not
// Methods starting with GET, POST, PUT, DELETE or HEAD should be mapped
func isMappedMethod(m reflect.Method) bool {
for _, httpMethod := range httpMethods {
if strings.HasPrefix(m.Name, httpMethod) {
return true
}
}
return false
}
// Splits the method name and returns
// the name of the HTTP method
// and the address of the method
// it is because Actions are addressable methods
func splitsMethodName(m *method) (string, string) {
var httpMethod, addr string
for _, httpMethod = range httpMethods {
if strings.HasPrefix(m.method.Name, httpMethod) {
slice := strings.Split(m.method.Name, httpMethod)
if len(slice) > 1 {
addr = slice[1]
}
return httpMethod, strings.ToLower(addr)
}
}
// If cant split it, return an error
log.Fatalf("Can't split the method %s name", m.method.Type)
return httpMethod, addr
}
// Write an error and Status Code in the ResponseWriter encoded as JSON,
func writeError(w http.ResponseWriter, err error, status int) {
// Encode the output in JSON
jsonResponse, err := json.MarshalIndent(map[string]string{"error": err.Error()}, "", "\t")
if err != nil {
http.Error(w, "{error: \"Error encoding the error message to Json: "+err.Error()+"\"}", http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
w.Write(jsonResponse)
}