summaryrefslogtreecommitdiff
path: root/pkg/registrar/registrar.go
blob: 1e75ee995b424f4f90be0a2efc95c3f3fb509705 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
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
}