-
Notifications
You must be signed in to change notification settings - Fork 110
/
subtype.go
141 lines (123 loc) · 3.31 KB
/
subtype.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
// Package subtype contains a Service type that can be used to hold all resources of a certain subtype.
package subtype
import (
"strings"
"sync"
"github.com/pkg/errors"
"go.viam.com/rdk/resource"
)
// Service defines an service that holds and replaces resources.
type Service interface {
Resource(name string) interface{}
ReplaceAll(resources map[resource.Name]interface{}) error
Add(name resource.Name, iface interface{}) error
Remove(name resource.Name) error
ReplaceOne(n resource.Name, iface interface{}) error
}
type subtypeSvc struct {
mu sync.RWMutex
resources map[string]interface{}
shortNames map[string]string
}
// New creates a new subtype service, which holds and replaces resources belonging to that subtype.
func New(r map[resource.Name]interface{}) (Service, error) {
s := &subtypeSvc{}
if err := s.ReplaceAll(r); err != nil {
return nil, err
}
return s, nil
}
// Resource returns resource by name, if it exists.
func (s *subtypeSvc) Resource(name string) interface{} {
s.mu.RLock()
defer s.mu.RUnlock()
if resource, ok := s.resources[name]; ok {
return resource
}
// looking for remote resource matching the name
if resource, ok := s.resources[s.shortNames[name]]; ok {
return resource
}
return nil
}
// ReplaceAll replaces all resources with r.
func (s *subtypeSvc) ReplaceAll(r map[resource.Name]interface{}) error {
s.mu.Lock()
defer s.mu.Unlock()
resources := make(map[string]interface{}, len(r))
shortNames := make(map[string]string, len(r))
s.resources = resources
s.shortNames = shortNames
for n, v := range r {
if err := s.doAdd(n, v); err != nil {
return err
}
}
return nil
}
func (s *subtypeSvc) Add(n resource.Name, iface interface{}) error {
s.mu.Lock()
defer s.mu.Unlock()
return s.doAdd(n, iface)
}
func (s *subtypeSvc) Remove(n resource.Name) error {
s.mu.Lock()
defer s.mu.Unlock()
return s.doRemove(n)
}
func (s *subtypeSvc) ReplaceOne(n resource.Name, iface interface{}) error {
s.mu.Lock()
defer s.mu.Unlock()
err := s.doRemove(n)
if err != nil {
return err
}
return s.doAdd(n, iface)
}
func (s *subtypeSvc) doAdd(n resource.Name, iface interface{}) error {
if n.Name == "" {
return errors.Errorf("empty name used for resource: %s", n)
}
name := n.ShortName()
_, exists := s.resources[name]
if exists {
return errors.Errorf("resource %s already exists", n)
}
s.resources[name] = iface
shortcut := getShortcutName(name)
if shortcut != name {
if _, ok := s.shortNames[shortcut]; ok {
s.shortNames[shortcut] = ""
} else {
s.shortNames[shortcut] = name
}
}
return nil
}
func (s *subtypeSvc) doRemove(n resource.Name) error {
name := n.ShortName()
_, ok := s.resources[name]
if !ok {
return errors.Errorf("resource %s not found", name)
}
delete(s.resources, name)
shortcut := getShortcutName(name)
_, ok = s.shortNames[shortcut]
if ok {
delete(s.shortNames, shortcut)
}
// case: remote1:nameA and remote2:nameA both existed, and remote2:nameA is being deleted, restore shortcut to remote1:nameA
for k := range s.resources {
if shortcut == getShortcutName(k) && name != getShortcutName(k) {
if _, ok := s.shortNames[shortcut]; ok {
s.shortNames[shortcut] = ""
} else {
s.shortNames[shortcut] = k
}
}
}
return nil
}
func getShortcutName(name string) string {
return name[strings.LastIndexAny(name, ":")+1:]
}