summaryrefslogtreecommitdiff
path: root/pkg/registrar/registrar.go
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/registrar/registrar.go')
-rw-r--r--pkg/registrar/registrar.go127
1 files changed, 127 insertions, 0 deletions
diff --git a/pkg/registrar/registrar.go b/pkg/registrar/registrar.go
new file mode 100644
index 000000000..1e75ee995
--- /dev/null
+++ b/pkg/registrar/registrar.go
@@ -0,0 +1,127 @@
+// Package registrar provides name registration. It reserves a name to a given key.
+package registrar
+
+import (
+ "errors"
+ "sync"
+)
+
+var (
+ // ErrNameReserved is an error which is returned when a name is requested to be reserved that already is reserved
+ ErrNameReserved = errors.New("name is reserved")
+ // ErrNameNotReserved is an error which is returned when trying to find a name that is not reserved
+ ErrNameNotReserved = errors.New("name is not reserved")
+ // ErrNoSuchKey is returned when trying to find the names for a key which is not known
+ ErrNoSuchKey = errors.New("provided key does not exist")
+)
+
+// Registrar stores indexes a list of keys and their registered names as well as indexes names and the key that they are registered to
+// Names must be unique.
+// Registrar is safe for concurrent access.
+type Registrar struct {
+ idx map[string][]string
+ names map[string]string
+ mu sync.Mutex
+}
+
+// NewRegistrar creates a new Registrar with the an empty index
+func NewRegistrar() *Registrar {
+ return &Registrar{
+ idx: make(map[string][]string),
+ names: make(map[string]string),
+ }
+}
+
+// Reserve registers a key to a name
+// Reserve is idempotent
+// Attempting to reserve a key to a name that already exists results in an `ErrNameReserved`
+// A name reservation is globally unique
+func (r *Registrar) Reserve(name, key string) error {
+ r.mu.Lock()
+ defer r.mu.Unlock()
+
+ if k, exists := r.names[name]; exists {
+ if k != key {
+ return ErrNameReserved
+ }
+ return nil
+ }
+
+ r.idx[key] = append(r.idx[key], name)
+ r.names[name] = key
+ return nil
+}
+
+// Release releases the reserved name
+// Once released, a name can be reserved again
+func (r *Registrar) Release(name string) {
+ r.mu.Lock()
+ defer r.mu.Unlock()
+
+ key, exists := r.names[name]
+ if !exists {
+ return
+ }
+
+ for i, n := range r.idx[key] {
+ if n != name {
+ continue
+ }
+ r.idx[key] = append(r.idx[key][:i], r.idx[key][i+1:]...)
+ break
+ }
+
+ delete(r.names, name)
+
+ if len(r.idx[key]) == 0 {
+ delete(r.idx, key)
+ }
+}
+
+// Delete removes all reservations for the passed in key.
+// All names reserved to this key are released.
+func (r *Registrar) Delete(key string) {
+ r.mu.Lock()
+ for _, name := range r.idx[key] {
+ delete(r.names, name)
+ }
+ delete(r.idx, key)
+ r.mu.Unlock()
+}
+
+// GetNames lists all the reserved names for the given key
+func (r *Registrar) GetNames(key string) ([]string, error) {
+ r.mu.Lock()
+ defer r.mu.Unlock()
+
+ names, exists := r.idx[key]
+ if !exists {
+ return nil, ErrNoSuchKey
+ }
+ return names, nil
+}
+
+// Get returns the key that the passed in name is reserved to
+func (r *Registrar) Get(name string) (string, error) {
+ r.mu.Lock()
+ key, exists := r.names[name]
+ r.mu.Unlock()
+
+ if !exists {
+ return "", ErrNameNotReserved
+ }
+ return key, nil
+}
+
+// GetAll returns all registered names
+func (r *Registrar) GetAll() map[string][]string {
+ out := make(map[string][]string)
+
+ r.mu.Lock()
+ // copy index into out
+ for id, names := range r.idx {
+ out[id] = names
+ }
+ r.mu.Unlock()
+ return out
+}