wrote clerk

This commit is contained in:
Jay
2026-05-09 20:15:00 -04:00
parent c0c23715e6
commit 19c62682b9
5 changed files with 327 additions and 5 deletions
+153 -4
View File
@@ -2,12 +2,25 @@ package prism
import (
"context"
"errors"
"fmt"
"git.wisehodl.dev/jay/go-honeybee"
"git.wisehodl.dev/jay/go-mana-component"
"git.wisehodl.dev/jay/go-roots-ws"
"log/slog"
"sync"
"time"
)
// ----------------------------------------------------------------------------
// Errors
// ----------------------------------------------------------------------------
var (
ErrAlreadyStarted = errors.New("clerk already started")
ErrUnknownLabel = errors.New("unknown label")
)
// ----------------------------------------------------------------------------
// Types
// ----------------------------------------------------------------------------
@@ -51,12 +64,148 @@ type clerkRoutes = map[string][]chan InboundLetter
// Clerk
// ----------------------------------------------------------------------------
func NewClerk() *Clerk {
func NewClerk(
ctx context.Context,
inbox <-chan honeybee.InboxMessage,
knownLabels map[string]struct{},
handler slog.Handler,
) *Clerk {
ctx, cancel := context.WithCancel(
component.MustNew(ctx, "prism", "clerk"))
known := make(map[string]struct{}, len(knownLabels))
for label := range knownLabels {
known[label] = struct{}{}
}
c := &Clerk{
inbox: inbox,
known: known,
ctx: ctx,
cancel: cancel,
}
if handler != nil {
comp, ok := component.Get(ctx)
if ok {
c.logger = slog.New(handler).With(slog.Any("component", comp))
}
}
return c
}
func (c *Clerk) Subscribe(
labels map[string]struct{},
buffer int,
) (<-chan InboundLetter, error) {
c.mu.Lock()
defer c.mu.Unlock()
if c.started {
return nil, ErrAlreadyStarted
}
for label := range labels {
if _, ok := c.known[label]; !ok {
return nil, fmt.Errorf("%w: %s", ErrUnknownLabel, label)
}
}
subLabels := make(map[string]struct{}, len(labels))
for label := range labels {
subLabels[label] = struct{}{}
}
ch := make(chan InboundLetter, buffer)
c.pending = append(c.pending, clerkSub{ch: ch, labels: subLabels})
return ch, nil
}
func (c *Clerk) Start() error {
c.mu.Lock()
defer c.mu.Unlock()
if c.started {
return ErrAlreadyStarted
}
routes := make(clerkRoutes, len(c.known))
for _, sub := range c.pending {
for label := range sub.labels {
routes[label] = append(routes[label], sub.ch)
}
}
c.routes = routes
c.started = true
c.wg.Add(1)
go c.run()
return nil
}
func (c *Clerk) Subscribe() {}
func (c *Clerk) Close() {
c.cancel()
c.wg.Wait()
func (c *Clerk) Start() {}
c.mu.Lock()
defer c.mu.Unlock()
func (c *Clerk) Close() {}
for _, sub := range c.pending {
close(sub.ch)
}
// prevent double channel closes if Close() is called twice
c.pending = nil
}
func (c *Clerk) run() {
defer c.wg.Done()
for {
select {
case <-c.ctx.Done():
return
case msg, ok := <-c.inbox:
if !ok {
// inbox closed externally, close clerk
c.cancel()
return
}
labelBytes, err := envelope.GetLabel(msg.Data)
if err != nil {
if c.logger != nil {
c.logger.Warn("invalid envelope",
"peer_id", msg.ID,
"received_at", msg.ReceivedAt,
)
}
continue
}
subs, ok := c.routes[string(labelBytes)]
if !ok {
continue
}
letter := InboundLetter{
ID: msg.ID,
Data: msg.Data,
At: msg.ReceivedAt,
}
for _, ch := range subs {
select {
case ch <- letter:
case <-c.ctx.Done():
return
}
}
}
}
}