package pubsub import ( "context" "errors" "github.com/google/uuid" "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/internal/libs/queue" ) var ( // ErrUnsubscribed is returned by Next when the client has unsubscribed. ErrUnsubscribed = errors.New("subscription removed by client") // ErrTerminated is returned by Next when the subscription was terminated by // the publisher. ErrTerminated = errors.New("subscription terminated by publisher") ) // A Subscription represents a client subscription for a particular query. type Subscription struct { id string queue *queue.Queue // open until the subscription ends stopErr error // after queue is closed, the reason why } // newSubscription returns a new subscription with the given queue capacity. func newSubscription(quota, limit int) (*Subscription, error) { queue, err := queue.New(queue.Options{ SoftQuota: quota, HardLimit: limit, }) if err != nil { return nil, err } return &Subscription{ id: uuid.NewString(), queue: queue, }, nil } // Next blocks until a message is available, ctx ends, or the subscription // ends. Next returns ErrUnsubscribed if s was unsubscribed, ErrTerminated if // s was terminated by the publisher, or a context error if ctx ended without a // message being available. func (s *Subscription) Next(ctx context.Context) (Message, error) { next, err := s.queue.Wait(ctx) if errors.Is(err, queue.ErrQueueClosed) { return Message{}, s.stopErr } else if err != nil { return Message{}, err } return next.(Message), nil } // ID returns the unique subscription identifier for s. func (s *Subscription) ID() string { return s.id } // publish transmits msg to the subscriber. It reports a queue error if the // queue cannot accept any further messages. func (s *Subscription) publish(msg Message) error { return s.queue.Add(msg) } // stop terminates the subscription with the given error reason. func (s *Subscription) stop(err error) { if err == nil { panic("nil stop error") } s.stopErr = err s.queue.Close() } // Message glues data and events together. type Message struct { subID string data interface{} events []types.Event } // SubscriptionID returns the unique identifier for the subscription // that produced this message. func (msg Message) SubscriptionID() string { return msg.subID } // Data returns an original data published. func (msg Message) Data() interface{} { return msg.data } // Events returns events, which matched the client's query. func (msg Message) Events() []types.Event { return msg.events }