11package bus
22
33import (
4+ "fmt"
45 "sync"
56 "sync/atomic"
67)
78
8- var mapper sync.Map // holds key (event id, typed string) versus topic values
9+ var mapper sync.Map // holds key (event name - string) versus topic values
910
10- // internal interface that all the events must implement
11- type iEvent interface {
11+ // we allow developers to override event names. They should be careful about name collisions
12+ type iEventName interface {
1213 EventID () string //
13- Async () bool // if returns true, this event will be triggered by spinning a goroutine
14+ }
15+
16+ // if developers implement this interface, we're spinning a goroutine if the event says it is async
17+ type iAsync interface {
18+ Async () bool
1419}
1520
1621// Listener is being returned when you subscribe to a topic, so you can unsubscribe or access the parent topic
17- type Listener [T iEvent ] struct {
22+ type Listener [T any ] struct {
1823 parent * Topic [T ] // so we can call unsubscribe from parent
1924 callback func (event T ) // the function that we're going to call
2025}
2126
2227// Topic keeps the subscribers of one topic
23- type Topic [T iEvent ] struct {
28+ type Topic [T any ] struct {
2429 subs []* Listener [T ] // list of listeners
2530 rwMu sync.RWMutex // guards subs
2631 lisnsPool sync.Pool // a pool of listeners
2732}
2833
2934// NewTopic creates a new topic for a specie of events
30- func NewTopic [T iEvent ]() * Topic [T ] {
35+ func NewTopic [T any ]() * Topic [T ] {
3136 result := & Topic [T ]{}
3237 result .lisnsPool .New = func () any {
3338 return & Listener [T ]{
@@ -90,19 +95,26 @@ func (s *Listener[T]) Topic() *Topic[T] {
9095// Pub allows you to publish an event in that topic
9196func (b * Topic [T ]) Pub (event T ) {
9297 b .rwMu .RLock ()
93- for topic := range b .subs {
94- if event .Async () {
95- go b .subs [topic ].callback (event )
98+
99+ isAsync := false
100+ switch m := any (event ).(type ) {
101+ case iAsync :
102+ isAsync = m .Async ()
103+ }
104+
105+ for sub := range b .subs {
106+ if isAsync {
107+ go b .subs [sub ].callback (event )
96108 continue
97109 }
98110
99- b .subs [topic ].callback (event )
111+ b .subs [sub ].callback (event )
100112 }
101113 b .rwMu .RUnlock ()
102114}
103115
104116// Bus is being returned when you subscribe, so you can manually Unsub
105- type Bus [T iEvent ] struct {
117+ type Bus [T any ] struct {
106118 listener * Listener [T ]
107119 stop atomic.Uint32 // flag for unsubscribing after receiving one event
108120}
@@ -115,11 +127,20 @@ func (o *Bus[T]) Unsub() {
115127}
116128
117129// SubUnsub can be used if you need to unsubscribe immediately after receiving an event, by making your function return true
118- func SubUnsub [T iEvent ](callback func (event T ) bool ) * Bus [T ] {
130+ func SubUnsub [T any ](callback func (event T ) bool ) * Bus [T ] {
119131 var event T
120- topic , ok := mapper .Load (event .EventID ())
132+
133+ key := ""
134+ switch m := any (event ).(type ) {
135+ case iEventName :
136+ key = m .EventID ()
137+ default :
138+ key = fmt .Sprintf ("%T" , event )
139+ }
140+
141+ topic , ok := mapper .Load (key )
121142 if ! ok || topic == nil {
122- topic , _ = mapper .LoadOrStore (event . EventID () , NewTopic [T ]())
143+ topic , _ = mapper .LoadOrStore (key , NewTopic [T ]())
123144 }
124145
125146 var result Bus [T ]
@@ -140,11 +161,20 @@ func SubUnsub[T iEvent](callback func(event T) bool) *Bus[T] {
140161}
141162
142163// Sub subscribes a callback function to listen for a specie of events
143- func Sub [T iEvent ](callback func (event T )) * Bus [T ] {
164+ func Sub [T any ](callback func (event T )) * Bus [T ] {
144165 var event T
145- topic , ok := mapper .Load (event .EventID ())
166+
167+ key := ""
168+ switch m := any (event ).(type ) {
169+ case iEventName :
170+ key = m .EventID ()
171+ default :
172+ key = fmt .Sprintf ("%T" , event )
173+ }
174+
175+ topic , ok := mapper .Load (key )
146176 if ! ok || topic == nil {
147- topic , _ = mapper .LoadOrStore (event . EventID () , NewTopic [T ]())
177+ topic , _ = mapper .LoadOrStore (key , NewTopic [T ]())
148178 }
149179
150180 var result Bus [T ]
@@ -160,10 +190,19 @@ func Sub[T iEvent](callback func(event T)) *Bus[T] {
160190}
161191
162192// Pub publishes an event which will be dispatched to all listeners
163- func Pub [T iEvent ](event T ) {
164- topic , ok := mapper .Load (event .EventID ())
193+ func Pub [T any ](event T ) {
194+ key := ""
195+ switch m := any (event ).(type ) {
196+ case iEventName :
197+ key = m .EventID ()
198+ default :
199+ key = fmt .Sprintf ("%T" , event )
200+ }
201+
202+ topic , ok := mapper .Load (key )
165203 if ! ok || topic == nil { // create new topic, even if there are no listeners (otherwise we will have to panic)
166- topic , _ = mapper .LoadOrStore (event . EventID () , NewTopic [T ]())
204+ topic , _ = mapper .LoadOrStore (key , NewTopic [T ]())
167205 }
206+
168207 topic .(* Topic [T ]).Pub (event )
169208}
0 commit comments