-
Notifications
You must be signed in to change notification settings - Fork 669
/
aliases.go
144 lines (118 loc) · 3.27 KB
/
aliases.go
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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
// Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package ids
import (
"errors"
"fmt"
"sync"
)
var (
ErrNoIDWithAlias = errors.New("there is no ID with alias")
errNoAliasForID = errors.New("there is no alias for ID")
errAliasAlreadyMapped = errors.New("alias already mapped to an ID")
)
// AliaserReader allows one to lookup the aliases given to an ID.
type AliaserReader interface {
// Lookup returns the ID associated with alias
Lookup(alias string) (ID, error)
// PrimaryAlias returns the first alias of [id]
PrimaryAlias(id ID) (string, error)
// Aliases returns the aliases of an ID
Aliases(id ID) ([]string, error)
}
// AliaserWriter allows one to give an ID aliases. An ID can have arbitrarily
// many aliases; two IDs may not have the same alias.
type AliaserWriter interface {
// Alias gives [id] the alias [alias]
Alias(id ID, alias string) error
// RemoveAliases of the provided ID
RemoveAliases(id ID)
}
// Aliaser allows one to give an ID aliases and lookup the aliases given to an
// ID.
type Aliaser interface {
AliaserReader
AliaserWriter
// PrimaryAliasOrDefault returns the first alias of [id], or ID string as a
// default if no alias exists
PrimaryAliasOrDefault(id ID) string
}
type aliaser struct {
lock sync.RWMutex
dealias map[string]ID
aliases map[ID][]string
}
func NewAliaser() Aliaser {
return &aliaser{
dealias: make(map[string]ID),
aliases: make(map[ID][]string),
}
}
func (a *aliaser) Lookup(alias string) (ID, error) {
a.lock.RLock()
defer a.lock.RUnlock()
if id, ok := a.dealias[alias]; ok {
return id, nil
}
return ID{}, fmt.Errorf("%w: %s", ErrNoIDWithAlias, alias)
}
func (a *aliaser) PrimaryAlias(id ID) (string, error) {
a.lock.RLock()
defer a.lock.RUnlock()
aliases := a.aliases[id]
if len(aliases) == 0 {
return "", fmt.Errorf("%w: %s", errNoAliasForID, id)
}
return aliases[0], nil
}
func (a *aliaser) PrimaryAliasOrDefault(id ID) string {
alias, err := a.PrimaryAlias(id)
if err != nil {
return id.String()
}
return alias
}
func (a *aliaser) Aliases(id ID) ([]string, error) {
a.lock.RLock()
defer a.lock.RUnlock()
return a.aliases[id], nil
}
func (a *aliaser) Alias(id ID, alias string) error {
a.lock.Lock()
defer a.lock.Unlock()
if _, exists := a.dealias[alias]; exists {
return fmt.Errorf("%w: %s", errAliasAlreadyMapped, alias)
}
a.dealias[alias] = id
a.aliases[id] = append(a.aliases[id], alias)
return nil
}
func (a *aliaser) RemoveAliases(id ID) {
a.lock.Lock()
defer a.lock.Unlock()
aliases := a.aliases[id]
delete(a.aliases, id)
for _, alias := range aliases {
delete(a.dealias, alias)
}
}
// GetRelevantAliases returns the aliases with the redundant identity alias
// removed (each id is aliased to at least itself).
func GetRelevantAliases(aliaser Aliaser, ids []ID) (map[ID][]string, error) {
result := make(map[ID][]string, len(ids))
for _, id := range ids {
aliases, err := aliaser.Aliases(id)
if err != nil {
return nil, err
}
// remove the redundant alias where alias = id.
relevantAliases := make([]string, 0, len(aliases)-1)
for _, alias := range aliases {
if alias != id.String() {
relevantAliases = append(relevantAliases, alias)
}
}
result[id] = relevantAliases
}
return result, nil
}