-
Notifications
You must be signed in to change notification settings - Fork 0
/
hook.go
147 lines (122 loc) · 3.99 KB
/
hook.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
package hooks
import (
"fmt"
"reflect"
"github.com/satori/go.uuid"
)
type Hook struct {
// ID will be likely to be used at the future, internally, in order to transfer hooks from other machine to another
// (yes I have plans to make it net compatible.)
ID string
Owner *Hub
Name string // Multiple hooks can have the same name, is the event.
Source Source
Callback reflect.Value
// if remains zero then order matters on execution,
// they are defaulted to the "IDLE" which doesn't matters if you don't care,
// it has nothing to do with performance, is a matter of order.
// each group of hooks has its own group, so the priority is per Name in the HooksMap.
//
// Read-only value, use SetPriority if you want to alt it.
Priority Priority
// hiher number is the first.
// optional descriptionist fields
Description string
}
func newHook(owner *Hub, name string, callback interface{}) *Hook {
val := reflect.ValueOf(callback)
if typ := val.Type(); !isFunc(typ) {
panic("callback should be a function but got: " + typ.String())
}
source := ReadSource(val.Pointer())
id := uuid.NewV4()
return &Hook{
Owner: owner,
ID: id.String(),
Name: name,
Source: source,
Callback: val,
Priority: Idle,
Description: "",
}
}
func (h *Hook) SetPriority(priority Priority) *Hook {
oldP := h.Priority
h.Priority = priority
if oldP != priority {
h.Owner.sortHooks(h.Name)
}
return h
}
func (h *Hook) PrioritizeAboveOf(otherHook *Hook) {
h.SetPriority(otherHook.Priority + 1) // +1 is enough, we work with integers, so no need to check the priority "levels".
}
func (h *Hook) Run(payloads ...interface{}) ([]reflect.Value, error) {
return h.run(payloads...)
}
func (h *Hook) RunAsync(payloads ...interface{}) *HookAsyncRunner {
asyncRunner := new(HookAsyncRunner)
go func() {
returnValues, err := h.run(payloads...)
asyncRunner.fireComplete(returnValues, err)
}()
return asyncRunner
}
type (
HookAsyncResultListener func([]reflect.Value, error)
HookAsyncRunner struct {
completeListeners []HookAsyncResultListener
pendingReturnValues []reflect.Value
pendingErr error
}
)
func (runner *HookAsyncRunner) OnComplete(listeners ...HookAsyncResultListener) {
runner.completeListeners = append(runner.completeListeners, listeners...)
if runner.pendingReturnValues != nil || runner.pendingErr != nil {
runner.fireComplete(runner.pendingReturnValues, runner.pendingErr)
runner.pendingReturnValues = nil
runner.pendingErr = nil
}
}
func (runner *HookAsyncRunner) fireComplete(returnValues []reflect.Value, err error) {
// if fire before OnComplete registered, save the results to fire them on
// the first call of .OnComplete (can have multiple listeners)
if len(runner.completeListeners) == 0 {
runner.pendingReturnValues = returnValues
runner.pendingErr = err
return
}
for i := range runner.completeListeners {
runner.completeListeners[i](returnValues, err)
}
}
func (h *Hook) run(payloads ...interface{}) (returnValues []reflect.Value, err error) { // maybe return values are useless and confusing here.
returnValues, err = execFunc(h.Callback, payloads...)
if err != nil {
err = fmt.Errorf("error: %s\n callback metadata:\n name: %s\n file: %s\n line: %d\n notification: '%s'",
err.Error(), h.Source.Name, h.Source.File, h.Source.Line, h.Name)
}
return
}
func (h *Hook) Use(preProcessor interface{}) *Hook { // func(callback interface{}, payloads ...interface{})) *Hook {
// oldCallback := h.Callback
// oldNext := h.preProcessor
// newNext := preProcessor.(func(func(string), string))
// next := func(message string) {
// if oldNext.IsValid() {
// _, err := execFunc(oldNext, message)
// if err != nil {
// println(err.Error())
// }
// }
// // println("from next")
// newNext(func(newMessage string) {
// _, err := execFunc(oldCallback, newMessage)
// if err != nil {
// println(err.Error())
// }
// }, message)
// }
// h.preProcessor = reflect.ValueOf(next)
return h
}