86 lines
1.9 KiB
Go
86 lines
1.9 KiB
Go
package session
|
|
|
|
import (
|
|
"context"
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"strings"
|
|
"sync"
|
|
|
|
"github.com/simonfxr/pubsub"
|
|
)
|
|
|
|
var eventBus = pubsub.NewBus()
|
|
|
|
// Event represents a published event with topic and payload.
|
|
type Event struct {
|
|
Topic string
|
|
Payload string
|
|
}
|
|
|
|
// PublishEvent publishes an event to a specific topic.
|
|
func PublishEvent(topic, payload string) {
|
|
ev := Event{Topic: topic, Payload: payload}
|
|
eventBus.Publish(topic, ev)
|
|
|
|
// Publish to hierarchical wildcards
|
|
parts := strings.Split(topic, ".")
|
|
for i := len(parts) - 1; i >= 1; i-- {
|
|
wildcard := strings.Join(parts[:i], ".") + ".*"
|
|
eventBus.Publish(wildcard, ev)
|
|
}
|
|
eventBus.Publish("*", ev)
|
|
}
|
|
|
|
// SubscribeEvents returns a channel that receives all events matching the topic.
|
|
func SubscribeEvents(ctx context.Context, topic string) <-chan Event {
|
|
ch := make(chan Event, 64)
|
|
sub := eventBus.SubscribeChan(topic, ch, pubsub.KeepOpen)
|
|
go func() {
|
|
<-ctx.Done()
|
|
eventBus.Unsubscribe(sub)
|
|
close(ch)
|
|
}()
|
|
return ch
|
|
}
|
|
|
|
// EventValue adapts an event channel into BlockOnce-compatible (readFn, signalFn).
|
|
// A goroutine drains the channel; each arrival updates the value and fires the signal.
|
|
func EventValue(ch <-chan Event) (readFn func() ([]byte, string, error), signalFn func() <-chan struct{}) {
|
|
var mu sync.Mutex
|
|
var data []byte
|
|
var hash string
|
|
sig := make(chan struct{})
|
|
|
|
go func() {
|
|
for ev := range ch {
|
|
line := ev.Topic
|
|
if ev.Payload != "" {
|
|
line += " " + ev.Payload
|
|
}
|
|
d := []byte(line + "\n")
|
|
h := sha256.Sum256(d)
|
|
mu.Lock()
|
|
data = d
|
|
hash = hex.EncodeToString(h[:8])
|
|
close(sig)
|
|
sig = make(chan struct{})
|
|
mu.Unlock()
|
|
}
|
|
}()
|
|
|
|
readFn = func() ([]byte, string, error) {
|
|
mu.Lock()
|
|
defer mu.Unlock()
|
|
out := make([]byte, len(data))
|
|
copy(out, data)
|
|
return out, hash, nil
|
|
}
|
|
signalFn = func() <-chan struct{} {
|
|
mu.Lock()
|
|
defer mu.Unlock()
|
|
return sig
|
|
}
|
|
return
|
|
}
|