mirror of
https://github.com/bitechdev/ResolveSpec.git
synced 2025-12-13 17:10:36 +00:00
141 lines
3.3 KiB
Go
141 lines
3.3 KiB
Go
package eventbroker
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
"sync"
|
|
"sync/atomic"
|
|
|
|
"github.com/bitechdev/ResolveSpec/pkg/logger"
|
|
)
|
|
|
|
// SubscriptionID uniquely identifies a subscription
|
|
type SubscriptionID string
|
|
|
|
// subscription represents a single subscription with its handler and pattern
|
|
type subscription struct {
|
|
id SubscriptionID
|
|
pattern string
|
|
handler EventHandler
|
|
}
|
|
|
|
// subscriptionManager manages event subscriptions and pattern matching
|
|
type subscriptionManager struct {
|
|
mu sync.RWMutex
|
|
subscriptions map[SubscriptionID]*subscription
|
|
nextID atomic.Uint64
|
|
}
|
|
|
|
// newSubscriptionManager creates a new subscription manager
|
|
func newSubscriptionManager() *subscriptionManager {
|
|
return &subscriptionManager{
|
|
subscriptions: make(map[SubscriptionID]*subscription),
|
|
}
|
|
}
|
|
|
|
// Subscribe adds a new subscription
|
|
func (sm *subscriptionManager) Subscribe(pattern string, handler EventHandler) (SubscriptionID, error) {
|
|
if pattern == "" {
|
|
return "", fmt.Errorf("pattern cannot be empty")
|
|
}
|
|
if handler == nil {
|
|
return "", fmt.Errorf("handler cannot be nil")
|
|
}
|
|
|
|
id := SubscriptionID(fmt.Sprintf("sub-%d", sm.nextID.Add(1)))
|
|
|
|
sm.mu.Lock()
|
|
sm.subscriptions[id] = &subscription{
|
|
id: id,
|
|
pattern: pattern,
|
|
handler: handler,
|
|
}
|
|
sm.mu.Unlock()
|
|
|
|
logger.Info("Subscribed to pattern '%s' with ID: %s", pattern, id)
|
|
return id, nil
|
|
}
|
|
|
|
// Unsubscribe removes a subscription
|
|
func (sm *subscriptionManager) Unsubscribe(id SubscriptionID) error {
|
|
sm.mu.Lock()
|
|
defer sm.mu.Unlock()
|
|
|
|
if _, exists := sm.subscriptions[id]; !exists {
|
|
return fmt.Errorf("subscription not found: %s", id)
|
|
}
|
|
|
|
delete(sm.subscriptions, id)
|
|
logger.Info("Unsubscribed: %s", id)
|
|
return nil
|
|
}
|
|
|
|
// GetMatching returns all handlers that match the event type
|
|
func (sm *subscriptionManager) GetMatching(eventType string) []EventHandler {
|
|
sm.mu.RLock()
|
|
defer sm.mu.RUnlock()
|
|
|
|
var handlers []EventHandler
|
|
for _, sub := range sm.subscriptions {
|
|
if matchPattern(sub.pattern, eventType) {
|
|
handlers = append(handlers, sub.handler)
|
|
}
|
|
}
|
|
|
|
return handlers
|
|
}
|
|
|
|
// Count returns the number of active subscriptions
|
|
func (sm *subscriptionManager) Count() int {
|
|
sm.mu.RLock()
|
|
defer sm.mu.RUnlock()
|
|
return len(sm.subscriptions)
|
|
}
|
|
|
|
// Clear removes all subscriptions
|
|
func (sm *subscriptionManager) Clear() {
|
|
sm.mu.Lock()
|
|
defer sm.mu.Unlock()
|
|
sm.subscriptions = make(map[SubscriptionID]*subscription)
|
|
logger.Info("Cleared all subscriptions")
|
|
}
|
|
|
|
// matchPattern implements glob-style pattern matching for event types
|
|
// Patterns:
|
|
// - "*" matches any single segment
|
|
// - "a.b.c" matches exactly "a.b.c"
|
|
// - "a.*.c" matches "a.anything.c"
|
|
// - "a.b.*" matches any operation on a.b
|
|
// - "*" matches everything
|
|
//
|
|
// Event type format: schema.entity.operation (e.g., "public.users.create")
|
|
func matchPattern(pattern, eventType string) bool {
|
|
// Wildcard matches everything
|
|
if pattern == "*" {
|
|
return true
|
|
}
|
|
|
|
// Exact match
|
|
if pattern == eventType {
|
|
return true
|
|
}
|
|
|
|
// Split pattern and event type by dots
|
|
patternParts := strings.Split(pattern, ".")
|
|
eventParts := strings.Split(eventType, ".")
|
|
|
|
// Different number of parts can only match if pattern has wildcards
|
|
if len(patternParts) != len(eventParts) {
|
|
return false
|
|
}
|
|
|
|
// Match each part
|
|
for i := range patternParts {
|
|
if patternParts[i] != "*" && patternParts[i] != eventParts[i] {
|
|
return false
|
|
}
|
|
}
|
|
|
|
return true
|
|
}
|