package events import ( "sync" "golang.org/x/exp/slices" ) type ListenerFunc func(interface{}) type Listener struct { Event string Handle ListenerFunc } type EventBus struct { lock sync.RWMutex listeners map[string][]*Listener } func NewEventBus() *EventBus { return &EventBus{ listeners: map[string][]*Listener{}, } } // Subscribe a listener for the given event type func (eb *EventBus) Subscribe(event string, f ListenerFunc) *Listener { l := &Listener{event, f} eb.lock.Lock() if prev, found := eb.listeners[event]; !found { eb.listeners[event] = []*Listener{l} } else { eb.listeners[event] = append(prev, l) } eb.lock.Unlock() return l } // Unsubscribe removes a listener for the given event type func (eb *EventBus) Unsubscribe(l *Listener) { eb.lock.Lock() listeners := eb.listeners[l.Event] i := slices.Index(listeners, l) if i >= 0 { // TODO: Magari usare la tecnica di spostare l'ultimo alla posizione dell'elemento rimosso e accorciare lo slice, tanto alla fine l'ordine dei listener non conta veramente eb.listeners[l.Event] = append(listeners[:i], listeners[i+1:]...) } eb.lock.Unlock() } // Dispatch func (eb *EventBus) Dispatch(event string, data interface{}) { eb.lock.RLock() if listeners, found := eb.listeners[event]; found { // make a copy of "listeners" to pass to the goroutine and not block the dispatcher thread tmpListeners := append([]*Listener{}, listeners...) go func() { for _, listener := range tmpListeners { listener.Handle(data) } }() } eb.lock.RUnlock() }