Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 21 additions & 17 deletions event_bus.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"fmt"
"reflect"
"sync"

"github.com/bcw104/EventBus/wildcard"
)

//BusSubscriber defines subscription-related bus behavior
Expand Down Expand Up @@ -45,7 +47,7 @@ type eventHandler struct {
flagOnce bool
async bool
transactional bool
sync.Mutex // lock for an event handler - useful for running async callbacks serially
sync.Mutex // lock for an event handler - useful for running async callbacks serially
}

// New returns new EventBus with empty handlers.
Expand Down Expand Up @@ -131,23 +133,25 @@ func (bus *EventBus) Unsubscribe(topic string, handler interface{}) error {
func (bus *EventBus) Publish(topic string, args ...interface{}) {
bus.lock.Lock() // will unlock if handler is not found or always after setUpPublish
defer bus.lock.Unlock()
if handlers, ok := bus.handlers[topic]; ok && 0 < len(handlers) {
// Handlers slice may be changed by removeHandler and Unsubscribe during iteration,
// so make a copy and iterate the copied slice.
copyHandlers := make([]*eventHandler, 0, len(handlers))
copyHandlers = append(copyHandlers, handlers...)
for i, handler := range copyHandlers {
if handler.flagOnce {
bus.removeHandler(topic, i)
}
if !handler.async {
bus.doPublish(handler, topic, args...)
} else {
bus.wg.Add(1)
if handler.transactional {
handler.Lock()
for topicPattern, handlers := range bus.handlers {
if 0 < len(handlers) && wildcard.MatchSimple(topicPattern, topic) {
// Handlers slice may be changed by removeHandler and Unsubscribe during iteration,
// so make a copy and iterate the copied slice.
copyHandlers := make([]*eventHandler, 0, len(handlers))
copyHandlers = append(copyHandlers, handlers...)
for i, handler := range copyHandlers {
if handler.flagOnce {
bus.removeHandler(topic, i)
}
if !handler.async {
bus.doPublish(handler, topic, args...)
} else {
bus.wg.Add(1)
if handler.transactional {
handler.Lock()
}
go bus.doPublishAsync(handler, topic, args...)
}
go bus.doPublishAsync(handler, topic, args...)
}
}
}
Expand Down
67 changes: 67 additions & 0 deletions wildcard/match.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package wildcard

// MatchSimple - finds whether the text matches/satisfies the pattern string.
// supports only '*' wildcard in the pattern.
// considers a file system path as a flat name space.
func MatchSimple(pattern, name string) bool {
if pattern == "" {
return name == pattern
}
if pattern == "*" {
return true
}
rname := make([]rune, 0, len(name))
rpattern := make([]rune, 0, len(pattern))
for _, r := range name {
rname = append(rname, r)
}
for _, r := range pattern {
rpattern = append(rpattern, r)
}
simple := true // Does only wildcard '*' match.
return deepMatchRune(rname, rpattern, simple)
}

// Match - finds whether the text matches/satisfies the pattern string.
// supports '*' and '?' wildcards in the pattern string.
// unlike path.Match(), considers a path as a flat name space while matching the pattern.
// The difference is illustrated in the example here https://play.golang.org/p/Ega9qgD4Qz .
func Match(pattern, name string) (matched bool) {
if pattern == "" {
return name == pattern
}
if pattern == "*" {
return true
}
rname := make([]rune, 0, len(name))
rpattern := make([]rune, 0, len(pattern))
for _, r := range name {
rname = append(rname, r)
}
for _, r := range pattern {
rpattern = append(rpattern, r)
}
simple := false // Does extended wildcard '*' and '?' match.
return deepMatchRune(rname, rpattern, simple)
}

func deepMatchRune(str, pattern []rune, simple bool) bool {
for len(pattern) > 0 {
switch pattern[0] {
default:
if len(str) == 0 || str[0] != pattern[0] {
return false
}
case '?':
if len(str) == 0 && !simple {
return false
}
case '*':
return deepMatchRune(str, pattern[1:], simple) ||
(len(str) > 0 && deepMatchRune(str[1:], pattern, simple))
}
str = str[1:]
pattern = pattern[1:]
}
return len(str) == 0 && len(pattern) == 0
}
Loading