ollie/cmd/olliesrv/internal/session/event.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
}