This repository has been archived by the owner on May 18, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathnew.go
107 lines (87 loc) · 2.91 KB
/
new.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
package detour
import (
"fmt"
"net/http"
"reflect"
)
type (
createModel func() interface{}
monadicAction func(interface{}) Renderer
niladicAction func() Renderer
)
func NewFromFactory(inputModelFactory createModel, controllerAction interface{}) http.Handler {
expectedModelType := identifyInputModelArgumentType(controllerAction)
if expectedModelType == nil {
panic("Controller action must accept an input model.")
}
actualModelType := reflect.TypeOf(inputModelFactory())
if actualModelType != expectedModelType {
panic(fmt.Sprintf(
"Controller requires input model of type: [%v] Factory function provided input model of type: [%v]",
expectedModelType,
actualModelType,
))
}
return withFactory(controllerAction, inputModelFactory)
}
func New(controllerAction interface{}) http.Handler {
modelType := identifyInputModelArgumentType(controllerAction)
if modelType == nil {
return simple(controllerAction.(func() Renderer))
}
return withFactory(controllerAction, func() interface{} {
return reflect.New(modelType.Elem()).Interface()
})
}
func withFactory(controllerAction interface{}, input createModel) http.Handler {
callbackType := reflect.ValueOf(controllerAction)
var callback monadicAction = func(m interface{}) Renderer {
results := callbackType.Call([]reflect.Value{reflect.ValueOf(m)})
result := results[0]
if result.IsNil() {
return nil
}
return result.Elem().Interface().(Renderer)
}
return &actionHandler{controller: callback, generateNewInputModel: input}
}
func simple(controllerAction niladicAction) http.Handler {
return &actionHandler{
controller: func(interface{}) Renderer { return controllerAction() },
generateNewInputModel: func() interface{} { return nil },
}
}
func identifyInputModelArgumentType(action interface{}) reflect.Type {
actionType := reflect.TypeOf(action)
if !isMethod(actionType) {
panic("The action provided is not a func.")
}
if !returnsRenderer(actionType) {
panic("The return type must implement the detour.Renderer interface.")
}
argumentCount := actionType.NumIn()
if argumentCount == 0 {
return nil
}
if argumentCount > 1 {
panic("The callback provided must have no more than one argument.")
}
firstArgumentType := actionType.In(0)
if !isSinglePointerArgument(argumentCount, firstArgumentType) {
panic("The first argument to the controller callback must be a pointer type.")
}
return firstArgumentType
}
func isMethod(callback reflect.Type) bool {
return callback.Kind() == reflect.Func
}
func returnsRenderer(actionType reflect.Type) bool {
return actionType.NumOut() == 1 && actionType.Out(0).Implements(renderer)
}
var renderer = reflect.TypeOf((*Renderer)(nil)).Elem()
func isSinglePointerArgument(argumentCount int, firstArgumentType reflect.Type) bool {
return argumentCount == 1 && isPointer(firstArgumentType)
}
func isPointer(argumentType reflect.Type) bool {
return argumentType.Kind() == reflect.Ptr
}