main pico / pkg / pubsub / client.go
Antonio Mika  ·  2026-10-10
 1package pubsub
 2
 3import (
 4	"io"
 5	"iter"
 6	"sync"
 7
 8	"github.com/antoniomika/syncmap"
 9)
10
11func NewClient(ID string, rw io.ReadWriter, direction ChannelDirection, blockWrite, replay, keepAlive bool) *Client {
12	return &Client{
13		ID:         ID,
14		ReadWriter: rw,
15		Direction:  direction,
16		Channels:   syncmap.New[string, *Channel](),
17		Done:       make(chan struct{}),
18		finish:     make(chan struct{}),
19		Data:       make(chan ChannelMessage),
20		Replay:     replay,
21		BlockWrite: blockWrite,
22		KeepAlive:  keepAlive,
23	}
24}
25
26/*
27Client is the container for holding state between multiple devices.  A
28client has a direction (input, output, inputout) as well as a way to
29send data to all the associated channels.
30*/
31type Client struct {
32	ID         string
33	ReadWriter io.ReadWriter
34	Channels   *syncmap.Map[string, *Channel]
35	Direction  ChannelDirection
36	Done       chan struct{}
37	Data       chan ChannelMessage
38	Replay     bool
39	BlockWrite bool
40	KeepAlive  bool
41	once       sync.Once
42	onceData   sync.Once
43	finish     chan struct{}
44	finishOnce sync.Once
45}
46
47func (c *Client) GetChannels() iter.Seq2[string, *Channel] {
48	return c.Channels.Range
49}
50
51// Cleanup ends the client. Its ReadWriter stays open for whoever opened it
52// to close, so an ssh session can still send its exit status.
53func (c *Client) Cleanup() {
54	c.once.Do(func() {
55		close(c.Done)
56	})
57}
58
59// Finish ends a subscriber after it writes the message it was last sent, so
60// a publisher that leaves doesn't cut off its own last message.
61func (c *Client) Finish() {
62	c.finishOnce.Do(func() {
63		close(c.finish)
64	})
65}