-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathregistry.go
146 lines (114 loc) · 3.19 KB
/
registry.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
145
146
package staticplug
import (
"fmt"
"os"
"reflect"
"slices"
"strings"
"sync"
)
type registeredPlugin struct {
info PluginInfo
ptype reflect.Type
}
type Registry struct {
mu sync.RWMutex
plugins []*registeredPlugin
byName map[string]*registeredPlugin
}
// NewRegistry instantiates a new plugin registry.
func NewRegistry() *Registry {
return &Registry{
byName: map[string]*registeredPlugin{},
}
}
// Register adds a plugin to the registry. A new plugin instance is created in
// the course of the registration to validate the produced type.
func (r *Registry) Register(p Plugin) error {
info := p.PluginInfo()
if info.Name == "" {
return fmt.Errorf("%w: plugin name missing", os.ErrInvalid)
}
pluginType := reflect.TypeOf(p)
if inst, err := info.New(); err != nil {
return fmt.Errorf("instantiating plugin %q: %w", info.Name, err)
} else if instType := reflect.TypeOf(inst); pluginType != instType {
return fmt.Errorf("%w: instantiating plugin %q returned type %v, want %v", os.ErrInvalid, info.Name, instType, pluginType)
}
rp := ®isteredPlugin{
info: info,
ptype: pluginType,
}
r.mu.Lock()
defer r.mu.Unlock()
if _, ok := r.byName[info.Name]; ok {
return fmt.Errorf("%w: plugin %q already registered", os.ErrInvalid, info.Name)
}
r.plugins = append(r.plugins, rp)
r.byName[info.Name] = rp
slices.SortStableFunc(r.plugins, func(a, b *registeredPlugin) int {
// TODO: Switch to cmp.Compare.
if a.info.Priority < b.info.Priority {
return -1
} else if a.info.Priority > b.info.Priority {
return +1
}
return strings.Compare(a.info.Name, b.info.Name)
})
return nil
}
// MustRegister registers a plugin by receiving a plain and empty value of the
// plugin, i.e. without full initialization. In most cases the plugin package
// should invoke this function in its "init" function.
//
// func init() {
// registry.MustRegister(&myPlugin{})
// }
func (r *Registry) MustRegister(p Plugin) {
must0(r.Register(p))
}
// PluginNames returns the names of all registered plugins.
func (r *Registry) PluginNames() []string {
r.mu.RLock()
defer r.mu.RUnlock()
names := make([]string, 0, len(r.plugins))
for _, rp := range r.plugins {
names = append(names, rp.info.Name)
}
return names
}
// Plugins returns all registered plugins.
func (r *Registry) Plugins() []PluginInfo {
r.mu.RLock()
defer r.mu.RUnlock()
result := make([]PluginInfo, 0, len(r.plugins))
for _, rp := range r.plugins {
result = append(result, rp.info)
}
return result
}
// PluginByName returns a plugin by its name.
func (r *Registry) PluginByName(name string) (PluginInfo, bool) {
r.mu.RLock()
defer r.mu.RUnlock()
rp, ok := r.byName[name]
return rp.info, ok
}
// PluginsImplementing returns all plugins implementing a particular interface
// type. [TypeOfInterface] is used to determine the interface reflection type
// of the interface type.
func (r *Registry) PluginsImplementing(iface any) ([]PluginInfo, error) {
ifaceType, err := TypeOfInterface(iface)
if err != nil {
return nil, err
}
var result []PluginInfo
r.mu.RLock()
defer r.mu.RUnlock()
for _, rp := range r.plugins {
if rp.ptype.Implements(ifaceType) {
result = append(result, rp.info)
}
}
return result, nil
}