├── .idea
├── .gitignore
├── gobserve.iml
├── modules.xml
└── vcs.xml
├── LICENSE.txt
├── README.md
├── dispatcher.go
├── dispatcher_test.go
├── events.go
├── examples
├── concurrentStrategy.go
├── priorityStrategy.go
├── simple.go
└── unsubscriber.go
├── globals.go
├── go.mod
├── go.sum
├── subscriber.go
├── types.go
├── utils.go
└── utils_test.go
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 | # Editor-based HTTP Client requests
5 | /httpRequests/
6 | # Datasource local storage ignored files
7 | /dataSources/
8 | /dataSources.local.xml
9 |
--------------------------------------------------------------------------------
/.idea/gobserve.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Henri Larget
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Gobserve
2 | =====
3 |
4 | A simple event-dispatcher library.
5 |
6 | [](https://pkg.go.dev/github.com/decima/gobserve)
7 |
8 | ## Install
9 |
10 | to add the library to your project simply run :
11 |
12 | ```bash
13 | go get github.com/decima/gobserve
14 | ```
15 |
16 | ## example usage :
17 |
18 | ```go
19 | package main
20 |
21 | import (
22 | "github.com/decima/gobserve"
23 | "log"
24 | )
25 |
26 | type helloWorld int
27 |
28 | func (s helloWorld) Name() string {
29 | return "sayHello"
30 | }
31 |
32 | func main() {
33 | gobserve.Subscribe("sayHello", func(h helloWorld) error {
34 | log.Println("hello from 1")
35 | return nil
36 | })
37 | gobserve.Subscribe("sayHello", func(h helloWorld) error {
38 | log.Println("hello from 2")
39 | return nil
40 | })
41 |
42 | gobserve.DispatchConcurrent(helloWorld(1))
43 |
44 | }
45 | ```
46 |
47 | By default you can use the Subscribe and Dispatch methods in order to use a globally defined event Subscriber.
48 | But you can create your own dispatcher for more precise and independant control.
49 |
--------------------------------------------------------------------------------
/dispatcher.go:
--------------------------------------------------------------------------------
1 | package gobserve
2 |
3 | import (
4 | "sync"
5 | )
6 |
7 | type actionType[T EventInterface] func(T) error
8 | type subscriptions[T EventInterface] map[string]map[int]map[string]actionType[T]
9 |
10 | // This structure contains a map of every events registered in the event Dispatcher
11 | type EventDispatcher[T EventInterface] struct {
12 | subscriptions[T]
13 | }
14 |
15 | // NewDispatcher creates a new eventDispatcher for EventInterfaces
16 | func NewDispatcher() EventDispatcher[EventInterface] {
17 | return EventDispatcher[EventInterface]{
18 | subscriptions: subscriptions[EventInterface]{},
19 | }
20 | }
21 |
22 | //Dispatch sends an event through every concerned event subscribers
23 | func (e *EventDispatcher[T]) Dispatch(
24 | event T,
25 | processingStrategy ProcessingStrategy,
26 | ) []error {
27 | errorListMutex := sync.Mutex{}
28 | errorList := []error{}
29 | if _, ok := e.subscriptions[event.Name()]; !ok {
30 | return nil
31 | }
32 |
33 | actionsPerPriority := sortIntMap(e.subscriptions[event.Name()])
34 |
35 | wg := sync.WaitGroup{}
36 |
37 | for _, actionList := range actionsPerPriority {
38 |
39 | for _, action := range actionList {
40 | wg.Add(1)
41 |
42 | go func(action func(T) error) {
43 | err := action(event)
44 | if err != nil {
45 | errorListMutex.Lock()
46 | errorList = append(errorList, err)
47 | errorListMutex.Unlock()
48 | }
49 | wg.Done()
50 | }(action)
51 | if processingStrategy == Sequential {
52 | wg.Wait()
53 | }
54 | }
55 |
56 | if processingStrategy == PerPriorityConcurrent {
57 | wg.Wait()
58 | }
59 | }
60 | if processingStrategy == Concurrent {
61 | wg.Wait()
62 | }
63 |
64 | if len(errorList) == 0 {
65 | return nil
66 | }
67 | return errorList
68 | }
69 |
--------------------------------------------------------------------------------
/dispatcher_test.go:
--------------------------------------------------------------------------------
1 | package gobserve
2 |
3 | import (
4 | "sync"
5 | "testing"
6 | )
7 |
8 | const DEMO_EVENT_NAME = "demo"
9 |
10 | type TestEvent struct {
11 | mux *sync.Mutex
12 | callStack *[]string
13 | }
14 |
15 | func (t TestEvent) Name() string {
16 | return DEMO_EVENT_NAME
17 | }
18 |
19 | func TestDispatch(t *testing.T) {
20 |
21 | dispatcher := NewDispatcher()
22 |
23 | dispatcher.SubscribeWithPriority(DEMO_EVENT_NAME,
24 | func(event EventInterface) error {
25 | return func(evt TestEvent) error {
26 | *evt.callStack = append(*evt.callStack, "1")
27 |
28 | return nil
29 | }(event.(TestEvent))
30 | }, 0)
31 | dispatcher.SubscribeWithPriority(DEMO_EVENT_NAME,
32 | func(event EventInterface) error {
33 | return func(evt TestEvent) error {
34 | *evt.callStack = append(*evt.callStack, "2")
35 |
36 | return nil
37 | }(event.(TestEvent))
38 | }, 0)
39 | dispatcher.SubscribeWithPriority(DEMO_EVENT_NAME,
40 | func(event EventInterface) error {
41 | return func(evt TestEvent) error {
42 | *evt.callStack = append(*evt.callStack, "3")
43 |
44 | return nil
45 | }(event.(TestEvent))
46 | }, 0)
47 | callStack := []string{}
48 | dispatcher.Dispatch(TestEvent{callStack: &callStack}, Sequential)
49 |
50 | expected := []string{"1", "2", "3"}
51 |
52 | if len(expected) != len(callStack) {
53 | t.Errorf("Expected String(%v) is not same as"+
54 | " actual string (%v)", expected, callStack)
55 | }
56 | for i := 0; i < len(expected); i++ {
57 |
58 | if expected[i] != callStack[i] {
59 | t.Errorf("Expected slice (%v) is not same as"+
60 | " actual slice (%v)", expected, callStack)
61 | }
62 | }
63 |
64 | }
65 |
66 | func TestDispatch2(t *testing.T) {
67 |
68 | dispatcher := NewDispatcher()
69 |
70 | dispatcher.SubscribeWithPriority(DEMO_EVENT_NAME,
71 | func(event EventInterface) error {
72 | return func(evt TestEvent) error {
73 | *evt.callStack = append(*evt.callStack, "1")
74 |
75 | return nil
76 | }(event.(TestEvent))
77 | }, 1)
78 | dispatcher.SubscribeWithPriority(DEMO_EVENT_NAME,
79 | func(event EventInterface) error {
80 | return func(evt TestEvent) error {
81 | *evt.callStack = append(*evt.callStack, "2")
82 |
83 | return nil
84 | }(event.(TestEvent))
85 | }, 2)
86 | dispatcher.SubscribeWithPriority(DEMO_EVENT_NAME,
87 | func(event EventInterface) error {
88 | return func(evt TestEvent) error {
89 | *evt.callStack = append(*evt.callStack, "3")
90 |
91 | return nil
92 | }(event.(TestEvent))
93 | }, 3)
94 | callStack := []string{}
95 | dispatcher.Dispatch(TestEvent{callStack: &callStack}, Sequential)
96 |
97 | expected := []string{"3", "2", "1"}
98 |
99 | if len(expected) != len(callStack) {
100 | t.Errorf("Expected String(%v) is not same as"+
101 | " actual string (%v)", expected, callStack)
102 | }
103 | for i := 0; i < len(expected); i++ {
104 |
105 | if expected[i] != callStack[i] {
106 | t.Errorf("Expected slice (%v) is not same as"+
107 | " actual slice (%v)", expected, callStack)
108 | }
109 | }
110 | }
111 |
112 | func TestDispatch3(t *testing.T) {
113 |
114 | dispatcher := NewDispatcher()
115 |
116 | dispatcher.SubscribeWithPriority(DEMO_EVENT_NAME,
117 | func(event EventInterface) error {
118 | return func(evt TestEvent) error {
119 |
120 | *evt.callStack = append(*evt.callStack, "1")
121 |
122 | return nil
123 | }(event.(TestEvent))
124 | }, 1)
125 | dispatcher.SubscribeWithPriority(DEMO_EVENT_NAME,
126 | func(event EventInterface) error {
127 | return func(evt TestEvent) error {
128 | *evt.callStack = append(*evt.callStack, "2")
129 |
130 | return nil
131 | }(event.(TestEvent))
132 | }, 2)
133 | dispatcher.SubscribeWithPriority(DEMO_EVENT_NAME,
134 | func(event EventInterface) error {
135 | return func(evt TestEvent) error {
136 | *evt.callStack = append(*evt.callStack, "3")
137 |
138 | return nil
139 | }(event.(TestEvent))
140 | }, 1)
141 | callStack := []string{}
142 | dispatcher.Dispatch(TestEvent{callStack: &callStack}, PerPriorityConcurrent)
143 |
144 | if 3 != len(callStack) {
145 | t.Errorf("Expected Len(3) is not same as"+
146 | " actual slice (%v)", callStack)
147 | }
148 |
149 | if "2" != callStack[0] {
150 |
151 | t.Errorf("Expected 2 to be first in the list"+
152 | " got (%v)", callStack)
153 | }
154 |
155 | }
156 |
157 | func TestDispatch4(t *testing.T) {
158 | dispatcher := NewDispatcher()
159 |
160 | dispatcher.SubscribeWithPriority(DEMO_EVENT_NAME,
161 | func(event EventInterface) error {
162 | return func(evt TestEvent) error {
163 | evt.mux.Lock()
164 | *evt.callStack = append(*evt.callStack, "1")
165 | evt.mux.Unlock()
166 | return nil
167 | }(event.(TestEvent))
168 | }, 1)
169 | dispatcher.SubscribeWithPriority(DEMO_EVENT_NAME,
170 | func(event EventInterface) error {
171 | return func(evt TestEvent) error {
172 | evt.mux.Lock()
173 |
174 | *evt.callStack = append(*evt.callStack, "2")
175 | evt.mux.Unlock()
176 | return nil
177 | }(event.(TestEvent))
178 | }, 2)
179 | dispatcher.SubscribeWithPriority(DEMO_EVENT_NAME,
180 | func(event EventInterface) error {
181 | return func(evt TestEvent) error {
182 | evt.mux.Lock()
183 | *evt.callStack = append(*evt.callStack, "3")
184 | evt.mux.Unlock()
185 | return nil
186 | }(event.(TestEvent))
187 | }, 1)
188 | callStack := []string{}
189 | mutex := sync.Mutex{}
190 | dispatcher.Dispatch(TestEvent{callStack: &callStack, mux: &mutex}, Concurrent)
191 | if 3 != len(callStack) {
192 | t.Errorf("Expected Len(3) is not same as"+
193 | " actual slice (%v)", callStack)
194 | }
195 |
196 | }
197 |
--------------------------------------------------------------------------------
/events.go:
--------------------------------------------------------------------------------
1 | package gobserve
2 |
3 | //EventInterface This interface is the "event enveloppe" for dispatching elements through all subscribes
4 | type EventInterface interface {
5 | //Name the name of the event
6 | Name() string
7 | }
8 |
--------------------------------------------------------------------------------
/examples/concurrentStrategy.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "github.com/decima/gobserve"
6 | "log"
7 | )
8 |
9 | type VoteEvent struct {
10 | Result *int
11 | }
12 |
13 | func (v VoteEvent) Name() string {
14 | return "vote"
15 | }
16 |
17 | func main() {
18 | for i := 0; i < 50; i++ {
19 | func(i int) {
20 | gobserve.Subscribe("vote", func(v VoteEvent) error {
21 | if i%3 == 0 {
22 | *v.Result--
23 | fmt.Printf("I(%v) voted NO : %v\n", i, *v.Result)
24 |
25 | } else {
26 | *v.Result++
27 | fmt.Printf("I(%v) voted YES: %v\n", i, *v.Result)
28 | }
29 | return nil
30 | })
31 | }(i)
32 | }
33 |
34 | result := 0
35 | v := VoteEvent{
36 | Result: &result,
37 | }
38 | gobserve.DispatchConcurrent(v)
39 |
40 | log.Println(*v.Result)
41 | }
42 |
--------------------------------------------------------------------------------
/examples/priorityStrategy.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "github.com/decima/gobserve"
5 | "log"
6 | "time"
7 | )
8 |
9 | type RacePodium struct{}
10 |
11 | func (r RacePodium) Name() string {
12 | return "podium"
13 | }
14 |
15 | func main() {
16 |
17 | gobserve.SubscribeWithPriority("podium", func(RacePodium) error {
18 | log.Println("LAURA : I finished ex-ego with SARAH")
19 | time.Sleep(2 * time.Second)
20 |
21 | return nil
22 | }, 600)
23 |
24 | gobserve.SubscribeWithPriority("podium", func(RacePodium) error {
25 | log.Println("BOB : I finished Last")
26 | time.Sleep(2 * time.Second)
27 | return nil
28 | }, 400)
29 |
30 | gobserve.SubscribeWithPriority("podium", func(RacePodium) error {
31 | log.Println("JOHN : Almost first!")
32 | time.Sleep(2 * time.Second)
33 |
34 | return nil
35 |
36 | }, 800)
37 | gobserve.SubscribeWithPriority("podium", func(RacePodium) error {
38 | log.Println("KEN : C'mon barbie, let's go party, i'm first!")
39 | time.Sleep(2 * time.Second)
40 |
41 | return nil
42 |
43 | }, 1000)
44 | gobserve.SubscribeWithPriority("podium", func(RacePodium) error {
45 | log.Println("SARAH : I finished at the same time as LAURA")
46 | time.Sleep(2 * time.Second)
47 |
48 | return nil
49 |
50 | }, 600)
51 |
52 | gobserve.DispatchConcurrentPerPriority(RacePodium{})
53 |
54 | }
55 |
--------------------------------------------------------------------------------
/examples/simple.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "github.com/decima/gobserve"
5 | "log"
6 | "time"
7 | )
8 |
9 | type helloMessage struct {
10 | From string
11 | }
12 |
13 | func (s helloMessage) Name() string {
14 | return "sayHello"
15 | }
16 |
17 | func (s helloMessage) Source() string {
18 | return s.From
19 | }
20 |
21 | func main() {
22 |
23 | gobserve.SubscribeWithPriority("sayHello", func(h helloMessage) error {
24 | log.Println("I lately received Hello from " + h.Source())
25 | time.Sleep(2 * time.Second)
26 |
27 | return nil
28 | }, -100)
29 |
30 | gobserve.SubscribeWithPriority("sayHello", func(h helloMessage) error {
31 | log.Println("I first received Hello from " + h.Source())
32 | time.Sleep(2 * time.Second)
33 | return nil
34 | }, 200)
35 |
36 | gobserve.SubscribeWithPriority("sayHello", func(h helloMessage) error {
37 | log.Println("I received Hello from " + h.Source())
38 | time.Sleep(2 * time.Second)
39 |
40 | return nil
41 | }, 100)
42 |
43 | gobserve.DispatchSequential(helloMessage{From: "Henri"})
44 | }
45 |
--------------------------------------------------------------------------------
/examples/unsubscriber.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "github.com/decima/gobserve"
5 | "log"
6 | )
7 |
8 | type hello2Message struct {
9 | }
10 |
11 | func (s hello2Message) Name() string {
12 | return "sayHello2"
13 | }
14 |
15 | func main() {
16 |
17 | gobserve.SubscribeWithPriority("sayHello2", func(h hello2Message) error {
18 | log.Println("hello from subscriber with prio 3")
19 | return nil
20 | }, 3)
21 |
22 | subscription := gobserve.SubscribeWithPriority("sayHello2", func(h hello2Message) error {
23 | log.Println("hello from subscriber with prio 2")
24 |
25 | return nil
26 | }, 2)
27 |
28 | gobserve.SubscribeWithPriority("sayHello2", func(h hello2Message) error {
29 | log.Println("hello from subscriber with prio 1")
30 |
31 | return nil
32 | }, 1)
33 |
34 | gobserve.DispatchSequential(hello2Message{})
35 | gobserve.Unsubscribe(subscription)
36 | gobserve.DispatchSequential(hello2Message{})
37 | }
38 |
--------------------------------------------------------------------------------
/globals.go:
--------------------------------------------------------------------------------
1 | package gobserve
2 |
3 | //globalEventDispatcher is an instance of the EventDispatch.
4 | //its purpose is to simplify the usage of the event dispatcher.
5 | var globalEventDispatcher = NewDispatcher()
6 |
7 | //Subscribe attach an action to an event name given.
8 | //Internally, this function, will attach the action to a normalPriority (0)
9 | func Subscribe[T EventInterface](name string, action func(T) error) interface{} {
10 | return SubscribeWithPriority(name, action, NormalPriority)
11 | }
12 |
13 | //SubscribeWithPriority attach an action to an event name given and weights the priority
14 | //The higher the priority the first will the action be trigger.
15 | func SubscribeWithPriority[T EventInterface](name string, action func(T) error, priority int) interface{} {
16 | return globalEventDispatcher.SubscribeWithPriority(name, func(event EventInterface) error {
17 | return action(event.(T))
18 | }, priority)
19 | }
20 |
21 | //Unsubscribe removes the subscription previously made.
22 | func Unsubscribe(sub interface{}) {
23 | globalEventDispatcher.Unsubscribe(sub)
24 | }
25 |
26 | //Dispatch sends an event to every same name events.
27 | //Processing Strategies are defined for concurrency/sequential behaviors.
28 | func Dispatch[T EventInterface](event T, strategy ProcessingStrategy) []error {
29 | return globalEventDispatcher.Dispatch(event, strategy)
30 | }
31 |
32 | //DispatchSequential sends an event through every action in a sequential mode.
33 | func DispatchSequential[T EventInterface](event T) []error {
34 | return Dispatch(event, Sequential)
35 | }
36 |
37 | //DispatchConcurrentPerPriority processes every priority sequentially, but with concurrent working in the tasks.
38 | func DispatchConcurrentPerPriority[T EventInterface](event T) []error {
39 | return Dispatch(event, PerPriorityConcurrent)
40 | }
41 |
42 | func DispatchConcurrent[T EventInterface](event T) []error {
43 | return Dispatch(event, Concurrent)
44 | }
45 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/decima/gobserve
2 |
3 | go 1.18
4 |
5 | require (
6 | github.com/google/uuid v1.3.0 // indirect
7 | github.com/yuin/goldmark v1.4.1 // indirect
8 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
9 | golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f // indirect
10 | golang.org/x/sys v0.0.0-20211019181941-9d821ace8654 // indirect
11 | golang.org/x/tools v0.1.11 // indirect
12 | )
13 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
2 | github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
3 | github.com/yuin/goldmark v1.4.1 h1:/vn0k+RBvwlxEmP5E7SZMqNxPhfMVFEJiykr15/0XKM=
4 | github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
5 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
6 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
7 | golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f h1:OfiFi4JbukWwe3lzw+xunroH1mnC1e2Gy5cxNJApiSY=
8 | golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
9 | golang.org/x/sys v0.0.0-20211019181941-9d821ace8654 h1:id054HUawV2/6IGm2IV8KZQjqtwAOo2CYlOToYqa0d0=
10 | golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
11 | golang.org/x/tools v0.1.11 h1:loJ25fNOEhSXfHrpoGj91eCUThwdNX6u24rO1xnNteY=
12 | golang.org/x/tools v0.1.11/go.mod h1:SgwaegtQh8clINPpECJMqnxLv9I09HLqnW3RMqW0CA4=
13 |
--------------------------------------------------------------------------------
/subscriber.go:
--------------------------------------------------------------------------------
1 | package gobserve
2 |
3 | import "github.com/google/uuid"
4 |
5 | type Subscriber[T EventInterface] struct {
6 | eventName string
7 | priority int
8 | uniqueId string
9 | dispatcher *EventDispatcher[T]
10 | }
11 |
12 | //Subscribe attach an action to an event name given.
13 | //Internally, this function, will attach the action to a normalPriority (0)
14 | func (e *EventDispatcher[T]) Subscribe(name string, action func(T) error) Subscriber[T] {
15 | return e.SubscribeWithPriority(name, action, NormalPriority).(Subscriber[T])
16 | }
17 |
18 | //SubscribeWithPriority attach an action to an event name given and weights the priority
19 | //The higher the priority the first will the action be trigger.
20 | func (e *EventDispatcher[T]) SubscribeWithPriority(name string, action func(T) error, priority int) interface{} {
21 | if _, ok := e.subscriptions[name]; !ok {
22 | e.subscriptions[name] = map[int]map[string]actionType[T]{}
23 | }
24 | if _, ok := e.subscriptions[name][priority]; !ok {
25 | e.subscriptions[name][priority] = map[string]actionType[T]{}
26 | }
27 |
28 | uniqueId := uuid.NewString()
29 | e.subscriptions[name][priority][uniqueId] = action
30 |
31 | return Subscriber[T]{
32 | eventName: name,
33 | priority: priority,
34 | uniqueId: uniqueId,
35 | }
36 | }
37 |
38 | //Unsubscribe removes the subscription previously made.
39 | func (e *EventDispatcher[T]) Unsubscribe(rawSub interface{}) {
40 | sub := rawSub.(Subscriber[T])
41 | if _, ok := e.subscriptions[sub.eventName][sub.priority][sub.uniqueId]; ok {
42 | delete(e.subscriptions[sub.eventName][sub.priority], sub.uniqueId)
43 | }
44 | if len(e.subscriptions[sub.eventName][sub.priority]) == 0 {
45 | delete(e.subscriptions[sub.eventName], sub.priority)
46 | }
47 | if len(e.subscriptions[sub.eventName]) == 0 {
48 | delete(e.subscriptions, sub.eventName)
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/types.go:
--------------------------------------------------------------------------------
1 | package gobserve
2 |
3 | type (
4 | //ProcessingStrategy the type of the
5 | ProcessingStrategy int
6 | )
7 |
8 | const (
9 | HighPriority = 100
10 | //NormalPriority default priority
11 | NormalPriority = 0
12 | LowPriority = -100
13 |
14 | //Sequential runs every action sequentially.
15 | Sequential ProcessingStrategy = iota
16 | //Concurrent runs every action at once, regardless priority
17 | Concurrent
18 | //PerPriorityConcurrent runs every actions concurrently regarding every priority. The priority processing is sequential.
19 | PerPriorityConcurrent
20 | )
21 |
--------------------------------------------------------------------------------
/utils.go:
--------------------------------------------------------------------------------
1 | package gobserve
2 |
3 | import "sort"
4 |
5 | //sortIntMap converts a int map of any type to a reverse-ordered slice of the same elements.
6 | func sortIntMap[T any](items map[int]T) []T {
7 | listOfKeys := []int{}
8 |
9 | for k, _ := range items {
10 | listOfKeys = append(listOfKeys, k)
11 | }
12 | sort.Sort(sort.Reverse(sort.IntSlice(listOfKeys)))
13 |
14 | returnValue := []T{}
15 |
16 | for _, val := range listOfKeys {
17 | returnValue = append(returnValue, items[val])
18 | }
19 | return returnValue
20 | }
21 |
--------------------------------------------------------------------------------
/utils_test.go:
--------------------------------------------------------------------------------
1 | package gobserve
2 |
3 | import "testing"
4 |
5 | func TestSortIntMap(t *testing.T) {
6 | input := map[int]int{0: 0, 10: 10, 7: 7, 8: 8}
7 | expectedOutput := []int{10, 8, 7, 0}
8 |
9 | output := sortIntMap(input)
10 |
11 | if len(expectedOutput) != len(output) {
12 | t.Errorf("Expected String(%v) is not same as"+
13 | " actual string (%v)", expectedOutput, output)
14 | }
15 | for i := 0; i < len(output); i++ {
16 |
17 | if expectedOutput[i] != output[i] {
18 | t.Errorf("Expected slice (%v) is not same as"+
19 | " actual slice (%v)", expectedOutput, output)
20 | }
21 | }
22 |
23 | }
24 |
--------------------------------------------------------------------------------