2022-07-06 23:19:05 +02:00
|
|
|
package subscriptions
|
|
|
|
|
|
|
|
import (
|
|
|
|
"sync"
|
|
|
|
|
|
|
|
"github.com/pocketbase/pocketbase/tools/security"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Message defines a client's channel data.
|
|
|
|
type Message struct {
|
|
|
|
Name string
|
|
|
|
Data string
|
|
|
|
}
|
|
|
|
|
|
|
|
// Client is an interface for a generic subscription client.
|
|
|
|
type Client interface {
|
|
|
|
// Id Returns the unique id of the client.
|
|
|
|
Id() string
|
|
|
|
|
|
|
|
// Channel returns the client's communication channel.
|
|
|
|
Channel() chan Message
|
|
|
|
|
|
|
|
// Subscriptions returns all subscriptions to which the client has subscribed to.
|
|
|
|
Subscriptions() map[string]struct{}
|
|
|
|
|
|
|
|
// Subscribe subscribes the client to the provided subscriptions list.
|
|
|
|
Subscribe(subs ...string)
|
|
|
|
|
|
|
|
// Unsubscribe unsubscribes the client from the provided subscriptions list.
|
|
|
|
Unsubscribe(subs ...string)
|
|
|
|
|
|
|
|
// HasSubscription checks if the client is subscribed to `sub`.
|
|
|
|
HasSubscription(sub string) bool
|
|
|
|
|
|
|
|
// Set stores any value to the client's context.
|
|
|
|
Set(key string, value any)
|
|
|
|
|
|
|
|
// Get retrieves the key value from the client's context.
|
|
|
|
Get(key string) any
|
2023-01-18 15:41:33 +02:00
|
|
|
|
|
|
|
// Discard marks the client as "discarded", meaning that it
|
|
|
|
// shouldn't be used anymore for sending new messages.
|
|
|
|
//
|
|
|
|
// It is safe to call Discard() multiple times.
|
|
|
|
Discard()
|
|
|
|
|
|
|
|
// IsDiscarded indicates whether the client has been "discarded"
|
|
|
|
// and should no longer be used.
|
|
|
|
IsDiscarded() bool
|
2022-07-06 23:19:05 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// ensures that DefaultClient satisfies the Client interface
|
|
|
|
var _ Client = (*DefaultClient)(nil)
|
|
|
|
|
|
|
|
// DefaultClient defines a generic subscription client.
|
|
|
|
type DefaultClient struct {
|
|
|
|
mux sync.RWMutex
|
2023-01-18 15:41:33 +02:00
|
|
|
isDiscarded bool
|
2022-07-06 23:19:05 +02:00
|
|
|
id string
|
|
|
|
store map[string]any
|
|
|
|
channel chan Message
|
|
|
|
subscriptions map[string]struct{}
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewDefaultClient creates and returns a new DefaultClient instance.
|
|
|
|
func NewDefaultClient() *DefaultClient {
|
|
|
|
return &DefaultClient{
|
|
|
|
id: security.RandomString(40),
|
|
|
|
store: map[string]any{},
|
|
|
|
channel: make(chan Message),
|
|
|
|
subscriptions: make(map[string]struct{}),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-30 10:28:14 +02:00
|
|
|
// Id implements the [Client.Id] interface method.
|
2022-07-06 23:19:05 +02:00
|
|
|
func (c *DefaultClient) Id() string {
|
2023-01-18 15:41:33 +02:00
|
|
|
c.mux.RLock()
|
|
|
|
defer c.mux.RUnlock()
|
|
|
|
|
2022-07-06 23:19:05 +02:00
|
|
|
return c.id
|
|
|
|
}
|
|
|
|
|
2022-10-30 10:28:14 +02:00
|
|
|
// Channel implements the [Client.Channel] interface method.
|
2022-07-06 23:19:05 +02:00
|
|
|
func (c *DefaultClient) Channel() chan Message {
|
2023-01-18 15:41:33 +02:00
|
|
|
c.mux.RLock()
|
|
|
|
defer c.mux.RUnlock()
|
|
|
|
|
2022-07-06 23:19:05 +02:00
|
|
|
return c.channel
|
|
|
|
}
|
|
|
|
|
2022-10-30 10:28:14 +02:00
|
|
|
// Subscriptions implements the [Client.Subscriptions] interface method.
|
2022-07-06 23:19:05 +02:00
|
|
|
func (c *DefaultClient) Subscriptions() map[string]struct{} {
|
2022-10-30 10:28:14 +02:00
|
|
|
c.mux.RLock()
|
|
|
|
defer c.mux.RUnlock()
|
2022-07-06 23:19:05 +02:00
|
|
|
|
|
|
|
return c.subscriptions
|
|
|
|
}
|
|
|
|
|
2022-10-30 10:28:14 +02:00
|
|
|
// Subscribe implements the [Client.Subscribe] interface method.
|
2022-07-06 23:19:05 +02:00
|
|
|
//
|
|
|
|
// Empty subscriptions (aka. "") are ignored.
|
|
|
|
func (c *DefaultClient) Subscribe(subs ...string) {
|
|
|
|
c.mux.Lock()
|
|
|
|
defer c.mux.Unlock()
|
|
|
|
|
|
|
|
for _, s := range subs {
|
|
|
|
if s == "" {
|
|
|
|
continue // skip empty
|
|
|
|
}
|
|
|
|
|
|
|
|
c.subscriptions[s] = struct{}{}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-30 10:28:14 +02:00
|
|
|
// Unsubscribe implements the [Client.Unsubscribe] interface method.
|
2022-07-06 23:19:05 +02:00
|
|
|
//
|
|
|
|
// If subs is not set, this method removes all registered client's subscriptions.
|
|
|
|
func (c *DefaultClient) Unsubscribe(subs ...string) {
|
|
|
|
c.mux.Lock()
|
|
|
|
defer c.mux.Unlock()
|
|
|
|
|
|
|
|
if len(subs) > 0 {
|
|
|
|
for _, s := range subs {
|
|
|
|
delete(c.subscriptions, s)
|
|
|
|
}
|
|
|
|
} else {
|
2022-10-17 19:17:44 +02:00
|
|
|
// unsubscribe all
|
2022-07-06 23:19:05 +02:00
|
|
|
for s := range c.subscriptions {
|
|
|
|
delete(c.subscriptions, s)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-30 10:28:14 +02:00
|
|
|
// HasSubscription implements the [Client.HasSubscription] interface method.
|
2022-07-06 23:19:05 +02:00
|
|
|
func (c *DefaultClient) HasSubscription(sub string) bool {
|
2022-10-30 10:28:14 +02:00
|
|
|
c.mux.RLock()
|
|
|
|
defer c.mux.RUnlock()
|
2022-07-06 23:19:05 +02:00
|
|
|
|
|
|
|
_, ok := c.subscriptions[sub]
|
|
|
|
|
|
|
|
return ok
|
|
|
|
}
|
|
|
|
|
2022-10-30 10:28:14 +02:00
|
|
|
// Get implements the [Client.Get] interface method.
|
2022-07-06 23:19:05 +02:00
|
|
|
func (c *DefaultClient) Get(key string) any {
|
2022-10-30 10:28:14 +02:00
|
|
|
c.mux.RLock()
|
|
|
|
defer c.mux.RUnlock()
|
2022-07-06 23:19:05 +02:00
|
|
|
|
|
|
|
return c.store[key]
|
|
|
|
}
|
|
|
|
|
2022-10-30 10:28:14 +02:00
|
|
|
// Set implements the [Client.Set] interface method.
|
2022-07-06 23:19:05 +02:00
|
|
|
func (c *DefaultClient) Set(key string, value any) {
|
|
|
|
c.mux.Lock()
|
|
|
|
defer c.mux.Unlock()
|
|
|
|
|
|
|
|
c.store[key] = value
|
|
|
|
}
|
2023-01-18 15:41:33 +02:00
|
|
|
|
|
|
|
// Discard implements the [Client.Discard] interface method.
|
|
|
|
func (c *DefaultClient) Discard() {
|
|
|
|
c.mux.Lock()
|
|
|
|
defer c.mux.Unlock()
|
|
|
|
|
|
|
|
c.isDiscarded = true
|
|
|
|
}
|
|
|
|
|
|
|
|
// IsDiscarded implements the [Client.IsDiscarded] interface method.
|
|
|
|
func (c *DefaultClient) IsDiscarded() bool {
|
|
|
|
c.mux.RLock()
|
|
|
|
defer c.mux.RUnlock()
|
|
|
|
|
|
|
|
return c.isDiscarded
|
|
|
|
}
|