-
Notifications
You must be signed in to change notification settings - Fork 0
/
typeregistry.go
144 lines (130 loc) · 4.1 KB
/
typeregistry.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 2020 Vedran Vuk. All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.
// Package typeregistry implements a simple type registry.
package typeregistry
import (
"reflect"
"sort"
"sync"
"github.com/vedranvuk/errorex"
)
var (
// ErrTypeRegistry is the base typeregistry error.
ErrTypeRegistry = errorex.New("typeregistry")
// ErrNotFound is returned when a type was not found in the registry.
ErrNotFound = ErrTypeRegistry.WrapFormat("entry '%s' not found")
// ErrDuplicateEntry is returned when registering a type that is already
// registered.
ErrDuplicateEntry = ErrTypeRegistry.WrapFormat("entry '%s' already exists")
// ErrInvalidParam is returned when an invalid parameter was passed to
// a registration method.
ErrInvalidParam = ErrTypeRegistry.Wrap("invalid parameter")
)
// Registry is a simple Go type registry. Safe for concurrent use, no panic.
//
// Types can be registered under auto-generated or custom type names then
// retrieved under those names as a reflect Type, Value or an Interface.
type Registry struct {
mu sync.Mutex
entries map[string]reflect.Type
}
// New returns a new *Registry instance.
func New() *Registry {
p := &Registry{
mu: sync.Mutex{},
entries: make(map[string]reflect.Type),
}
return p
}
// GetLongTypeName generates a long Type name from a Go value contained in i
// thet is constructed of pointer dereferene token for each pointer level of i,
// package path if the type was not predeclared or is an alias and the type
// name as returned by reflect.Value.Type().String(). For example:
// ***Registry => "***github.com/vedranvuk/typeregistry/typeregistry.Registry".
//
// It is used to generate a type name for values registered by this package.
func GetLongTypeName(i interface{}) (r string) {
if i == nil {
return
}
v := reflect.ValueOf(i)
for v.Kind() == reflect.Ptr && !v.IsZero() {
r += "*"
v = v.Elem()
}
if s := v.Type().PkgPath(); s != "" {
r += s + "/"
}
r += v.Type().String()
return
}
// Register registers a reflect.Type of value specified by v under a name
// generated by GetLongTypeName or returns an error.
func (r *Registry) Register(v interface{}) error {
return r.RegisterNamed(GetLongTypeName(v), v)
}
// RegisterNamed registers a reflect.Type of value specified by v under
// specified name, which cannot be empty, or returns an error.
func (r *Registry) RegisterNamed(name string, v interface{}) error {
if name == "" || v == nil {
return ErrInvalidParam
}
r.mu.Lock()
defer r.mu.Unlock()
if _, exists := r.entries[name]; exists {
return ErrDuplicateEntry.WrapArgs(name)
}
r.entries[name] = reflect.TypeOf(v)
return nil
}
// Unregister unregisters a reflect.Type registered under specified name or
// returns an error.
func (r *Registry) Unregister(name string) error {
r.mu.Lock()
defer r.mu.Unlock()
if _, exists := r.entries[name]; !exists {
return ErrNotFound.WrapArgs(name)
}
delete(r.entries, name)
return nil
}
// GetType returns a registered reflect.Type specified by name or an error.
func (r *Registry) GetType(name string) (reflect.Type, error) {
r.mu.Lock()
defer r.mu.Unlock()
t, ok := r.entries[name]
if !ok {
return nil, ErrNotFound.WrapArgs(name)
}
return t, nil
}
// GetValue returns a new reflect.Value of reflect.Type registered under
// specified name or an error.
func (r *Registry) GetValue(name string) (reflect.Value, error) {
t, err := r.GetType(name)
if err != nil {
return reflect.Value{}, err
}
return reflect.New(t).Elem(), nil
}
// GetInterface returns an interface to a new reflect.Value of reflect.Type
// registered under specified name or an error.
func (r *Registry) GetInterface(name string) (interface{}, error) {
t, err := r.GetType(name)
if err != nil {
return reflect.Value{}, err
}
return reflect.New(t).Elem().Interface(), nil
}
// RegisteredNames returns a slice of registered type names.
func (r *Registry) RegisteredNames() []string {
r.mu.Lock()
defer r.mu.Unlock()
names := make([]string, 0, len(r.entries))
for key := range r.entries {
names = append(names, key)
}
sort.Strings(names)
return names
}