Wrote embassy

This commit is contained in:
Jay
2026-05-09 17:36:03 -04:00
parent e14f2b83a5
commit e909e140a8
6 changed files with 477 additions and 15 deletions
+186 -9
View File
@@ -2,7 +2,9 @@ package prism
import (
"context"
"fmt"
"git.wisehodl.dev/jay/go-honeybee"
"git.wisehodl.dev/jay/go-mana-component"
"log/slog"
"sync"
"time"
@@ -39,6 +41,7 @@ type PoolEventKind = int
const (
EventConnected PoolEventKind = iota
EventDisconnected
EventAdded
EventRemoved
)
@@ -48,6 +51,15 @@ type PoolEvent struct {
At time.Time
}
func NewPoolEvent(id string, kind PoolEventKind, at time.Time) PoolEvent {
return PoolEvent{ID: id, Kind: kind, At: at}
}
var convertPoolEvent = map[honeybee.OutboundPoolEventKind]PoolEventKind{
honeybee.OutboundEventConnected: EventConnected,
honeybee.OutboundEventDisconnected: EventDisconnected,
}
// Adapter
type Adapter interface {
@@ -92,40 +104,205 @@ type Hotel struct {
// Embassy (Outbound Adapter)
// ----------------------------------------------------------------------------
func NewEmbassy() *Embassy {
return nil
func NewEmbassy(
ctx context.Context,
pool EmbassyPlugin,
jc *JournalCollector,
handler slog.Handler,
) *Embassy {
ctx, cancel := context.WithCancel(
component.MustNew(ctx, "prism", "embassy"))
e := &Embassy{
pool: pool,
peers: make(map[string]bool),
eventSubs: make([]chan PoolEvent, 0),
ctx: ctx,
cancel: cancel,
}
if jc != nil {
e.journals = make(chan JournalEntry, 16)
jc.Enroll(e.journals)
}
if handler != nil {
c, ok := component.Get(ctx)
if ok {
e.logger = slog.New(handler).With(slog.Any("component", c))
}
}
e.wg.Add(1)
go e.runEventRouter()
return e
}
func (e *Embassy) Dispatch(url string) error {
url, err := honeybee.NormalizeURL(url)
if err != nil {
return fmt.Errorf("invalid url: %s", url)
}
if err := e.pool.Connect(url); err != nil {
return fmt.Errorf("dispatch: %w", err)
}
e.mu.Lock()
e.peers[url] = false
subs := e.eventSubs
e.mu.Unlock()
for _, ch := range subs {
select {
case <-e.ctx.Done():
return fmt.Errorf("closing")
case ch <- NewPoolEvent(url, EventAdded, time.Now()):
}
}
return nil
}
func (e *Embassy) Dismiss(url string) error {
url, err := honeybee.NormalizeURL(url)
if err != nil {
return fmt.Errorf("invalid url: %s", url)
}
if err := e.pool.Remove(url); err != nil {
return fmt.Errorf("dismiss: %w", err)
}
e.mu.Lock()
delete(e.peers, url)
subs := e.eventSubs
e.mu.Unlock()
for _, ch := range subs {
select {
case <-e.ctx.Done():
return fmt.Errorf("closing")
case ch <- NewPoolEvent(url, EventRemoved, time.Now()):
}
}
return nil
}
func (e *Embassy) Close() {}
func (e *Embassy) Close() {
e.mu.Lock()
peers := e.peers
e.peers = make(map[string]bool)
e.mu.Unlock()
// dismiss peers
for peer, _ := range peers {
e.Dismiss(peer)
}
e.cancel()
e.wg.Wait()
e.mu.Lock()
subs := e.eventSubs
e.eventSubs = make([]chan PoolEvent, 0)
e.mu.Unlock()
// close subs
for _, sub := range subs {
close(sub)
}
}
func (e *Embassy) Peers() []string {
return nil
e.mu.RLock()
defer e.mu.RUnlock()
peers := make([]string, 0, len(e.peers))
for p, _ := range e.peers {
peers = append(peers, p)
}
return peers
}
func (e *Embassy) HasPeer(id string) bool {
return false
func (e *Embassy) HasPeer(url string) bool {
url, err := honeybee.NormalizeURL(url)
if err != nil {
return false
}
e.mu.RLock()
defer e.mu.RUnlock()
_, ok := e.peers[url]
return ok
}
func (e *Embassy) IsConnected(id string) bool {
return false
func (e *Embassy) IsConnected(url string) bool {
url, err := honeybee.NormalizeURL(url)
if err != nil {
return false
}
e.mu.RLock()
defer e.mu.RUnlock()
connected, _ := e.peers[url]
return connected
}
func (e *Embassy) Subscribe() <-chan PoolEvent {
return nil
e.mu.Lock()
defer e.mu.Unlock()
ch := make(chan PoolEvent, 16)
e.eventSubs = append(e.eventSubs, ch)
return ch
}
func (e *Embassy) Send(id string, data Envelope) error {
return nil
}
// Internal
func (e *Embassy) runEventRouter() {
defer e.wg.Done()
for {
select {
case <-e.ctx.Done():
return
case ev, ok := <-e.pool.Events:
if !ok {
return
}
kind := convertPoolEvent[ev.Kind]
e.mu.Lock()
switch kind {
case EventConnected:
e.peers[ev.ID] = true
case EventDisconnected:
e.peers[ev.ID] = false
}
subs := e.eventSubs
e.mu.Unlock()
for _, ch := range subs {
select {
case <-e.ctx.Done():
case ch <- NewPoolEvent(ev.ID, kind, ev.At):
}
}
}
}
}
// ----------------------------------------------------------------------------
// Hotel (Inbound Adapter)
// ----------------------------------------------------------------------------