/
registry.go
105 lines (94 loc) · 3.24 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
// Copyright 2014 Canonical Ltd.
// Licensed under the LGPLv3, see LICENCE file for details.
package registry
import (
"fmt"
"reflect"
"sort"
"github.com/juju/errors"
)
// TypedNameVersion is a registry that will allow you to register objects based
// on a name and version pair. The objects must be convertible to the Type
// defined when the registry was created. It will be cast during Register so
// you can be sure all objects returned from Get() are safe to TypeAssert to
// that type.
type TypedNameVersion struct {
requiredType reflect.Type
versions map[string]Versions
}
// NewTypedNameVersion creates a place to register your objects
func NewTypedNameVersion(requiredType reflect.Type) *TypedNameVersion {
return &TypedNameVersion{
requiredType: requiredType,
versions: make(map[string]Versions),
}
}
// Description gives the name and available versions in a registry.
type Description struct {
Name string
Versions []int
}
// Versions maps concrete versions of the objects.
type Versions map[int]interface{}
// Register records the factory that can be used to produce an instance of the
// facade at the supplied version.
// If the object being registered doesn't Implement the required Type, then an
// error is returned.
// An error is also returned if an object is already registered with the given
// name and version.
func (r *TypedNameVersion) Register(name string, version int, obj interface{}) error {
if !reflect.TypeOf(obj).ConvertibleTo(r.requiredType) {
return fmt.Errorf("object of type %T cannot be converted to type %s.%s", obj, r.requiredType.PkgPath(), r.requiredType.Name())
}
obj = reflect.ValueOf(obj).Convert(r.requiredType).Interface()
if r.versions == nil {
r.versions = make(map[string]Versions, 1)
}
if versions, ok := r.versions[name]; ok {
if _, ok := versions[version]; ok {
fullname := fmt.Sprintf("%s(%d)", name, version)
return fmt.Errorf("object %q already registered", fullname)
}
versions[version] = obj
} else {
r.versions[name] = Versions{version: obj}
}
return nil
}
// descriptionFromVersions aggregates the information in a Versions map into a
// more friendly form for List()
func descriptionFromVersions(name string, versions Versions) Description {
intVersions := make([]int, 0, len(versions))
for version := range versions {
intVersions = append(intVersions, version)
}
sort.Ints(intVersions)
return Description{
Name: name,
Versions: intVersions,
}
}
// List returns a slice describing each of the registered Facades.
func (r *TypedNameVersion) List() []Description {
names := make([]string, 0, len(r.versions))
for name := range r.versions {
names = append(names, name)
}
sort.Strings(names)
descriptions := make([]Description, len(r.versions))
for i, name := range names {
versions := r.versions[name]
descriptions[i] = descriptionFromVersions(name, versions)
}
return descriptions
}
// Get returns the object for a single name and version. If the requested
// facade is not found, it returns error.NotFound
func (r *TypedNameVersion) Get(name string, version int) (interface{}, error) {
if versions, ok := r.versions[name]; ok {
if factory, ok := versions[version]; ok {
return factory, nil
}
}
return nil, errors.NotFoundf("%s(%d)", name, version)
}