-
Notifications
You must be signed in to change notification settings - Fork 177
/
event_dispatcher.go
135 lines (106 loc) · 3.32 KB
/
event_dispatcher.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 ext
import (
"context"
"errors"
"fmt"
"strings"
)
type Event string
type EventHandlerFn[T any] func(ctx context.Context, args T) error
var (
ErrInvalidEvent = errors.New("invalid event name for the current type")
)
type EventDispatcher[T any] struct {
handlers map[Event][]EventHandlerFn[T]
eventNames map[Event]struct{}
}
func NewEventDispatcher[T any](validEventNames ...Event) *EventDispatcher[T] {
eventNames := map[Event]struct{}{}
for _, name := range validEventNames {
eventNames[name] = struct{}{}
eventNames[Event("pre"+name)] = struct{}{}
eventNames[Event("post"+name)] = struct{}{}
}
return &EventDispatcher[T]{
handlers: map[Event][]EventHandlerFn[T]{},
eventNames: eventNames,
}
}
// Adds an event handler for the specified event name
func (ed *EventDispatcher[T]) AddHandler(name Event, handler EventHandlerFn[T]) error {
if err := ed.validateEvent(name); err != nil {
return err
}
events := ed.handlers[name]
events = append(events, handler)
ed.handlers[name] = events
return nil
}
// Removes the event handler for the specified event name
func (ed *EventDispatcher[T]) RemoveHandler(name Event, handler EventHandlerFn[T]) error {
if err := ed.validateEvent(name); err != nil {
return err
}
newHandler := fmt.Sprintf("%v", handler)
events := ed.handlers[name]
for i, ref := range events {
existingHandler := fmt.Sprintf("%v", ref)
if newHandler == existingHandler {
ed.handlers[name] = append(events[:i], events[i+1:]...)
return nil
}
}
return fmt.Errorf("specified handler was not found in %s event registrations", name)
}
// Raises the specified event and calls any registered event handlers
func (ed *EventDispatcher[T]) RaiseEvent(ctx context.Context, name Event, eventArgs T) error {
if err := ed.validateEvent(name); err != nil {
return err
}
handlerErrors := []error{}
handlers := ed.handlers[name]
// TODO: Opportunity to dispatch these event handlers in parallel if needed
for _, handler := range handlers {
err := handler(ctx, eventArgs)
if err != nil {
handlerErrors = append(handlerErrors, err)
}
}
// Build final error string if their are any failures
if len(handlerErrors) > 0 {
lines := make([]string, len(handlerErrors))
for i, err := range handlerErrors {
lines[i] = err.Error()
}
return errors.New(strings.Join(lines, ","))
}
return nil
}
// Invokes an action and raises an event before and after the action
func (ed *EventDispatcher[T]) Invoke(ctx context.Context, name Event, eventArgs T, action InvokeFn) error {
if err := ed.validateEvent(name); err != nil {
return err
}
preEventName := Event(fmt.Sprintf("pre%s", name))
postEventName := Event(fmt.Sprintf("post%s", name))
if err := ed.RaiseEvent(ctx, preEventName, eventArgs); err != nil {
return fmt.Errorf("failed invoking event handlers for 'pre%s', %w", name, err)
}
if err := action(); err != nil {
return err
}
if err := ed.RaiseEvent(ctx, postEventName, eventArgs); err != nil {
return fmt.Errorf("failed invoking event handlers for 'post%s', %w", name, err)
}
return nil
}
func (ed *EventDispatcher[T]) validateEvent(name Event) error {
// If not events have been defined assumed any event name is valid
if len(ed.eventNames) == 0 {
return nil
}
if _, has := ed.eventNames[name]; !has {
return fmt.Errorf("%s: %w", name, ErrInvalidEvent)
}
return nil
}