github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/apiserver/facade/registry.go (about) 1 // Copyright 2016 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package facade 5 6 import ( 7 "fmt" 8 "reflect" 9 "sort" 10 11 "github.com/juju/errors" 12 ) 13 14 // record represents an entry in a Registry. 15 type record struct { 16 factory Factory 17 facadeType reflect.Type 18 } 19 20 // versions is our internal structure for tracking specific versions of a 21 // single facade. We use a map to be able to quickly lookup a version. 22 type versions map[int]record 23 24 // FacadeRegistry describes the API facades exposed by some API server. 25 type FacadeRegistry interface { 26 // MustRegister adds a single named facade at a given version to the 27 // registry. 28 // Factory will be called when someone wants to instantiate an object of 29 // this facade, and facadeType defines the concrete type that the returned 30 // object will be. 31 // The Type information is used to define what methods will be exported in 32 // the API, and it must exactly match the actual object returned by the 33 // factory. 34 MustRegister(string, int, Factory, reflect.Type) 35 } 36 37 // Registry describes the API facades exposed by some API server. 38 type Registry struct { 39 facades map[string]versions 40 } 41 42 // Register adds a single named facade at a given version to the registry. 43 // Factory will be called when someone wants to instantiate an object of 44 // this facade, and facadeType defines the concrete type that the returned 45 // object will be. 46 // The Type information is used to define what methods will be exported in the 47 // API, and it must exactly match the actual object returned by the factory. 48 func (f *Registry) Register(name string, version int, factory Factory, facadeType reflect.Type) error { 49 if f.facades == nil { 50 f.facades = make(map[string]versions, 1) 51 } 52 record := record{ 53 factory: factory, 54 facadeType: facadeType, 55 } 56 if vers, ok := f.facades[name]; ok { 57 if _, ok := vers[version]; ok { 58 fullname := fmt.Sprintf("%s(%d)", name, version) 59 return fmt.Errorf("object %q already registered", fullname) 60 } 61 vers[version] = record 62 } else { 63 f.facades[name] = versions{version: record} 64 } 65 return nil 66 } 67 68 // MustRegister adds a single named facade at a given version to the registry 69 // and panics if it fails. 70 // See: Register. 71 func (f *Registry) MustRegister(name string, version int, factory Factory, facadeType reflect.Type) { 72 if err := f.Register(name, version, factory, facadeType); err != nil { 73 panic(err) 74 } 75 } 76 77 // lookup translates a facade name and version into a record. 78 func (f *Registry) lookup(name string, version int) (record, error) { 79 if versions, ok := f.facades[name]; ok { 80 if record, ok := versions[version]; ok { 81 return record, nil 82 } 83 } 84 return record{}, errors.NotFoundf("%s(%d)", name, version) 85 } 86 87 // GetFactory returns just the Factory for a given Facade name and version. 88 // See also GetType for getting the type information instead of the creation factory. 89 func (f *Registry) GetFactory(name string, version int) (Factory, error) { 90 record, err := f.lookup(name, version) 91 if err != nil { 92 return nil, err 93 } 94 return record.factory, nil 95 } 96 97 // GetType returns the type information for a given Facade name and version. 98 // This can be used for introspection purposes (to determine what methods are 99 // available, etc). 100 func (f *Registry) GetType(name string, version int) (reflect.Type, error) { 101 record, err := f.lookup(name, version) 102 if err != nil { 103 return nil, err 104 } 105 return record.facadeType, nil 106 } 107 108 // Description describes the name and what versions of a facade have been 109 // registered. 110 type Description struct { 111 Name string 112 Versions []int 113 } 114 115 // descriptionFromVersions aggregates the information in a versions map into a 116 // more friendly form for List(). 117 func descriptionFromVersions(name string, vers versions) Description { 118 intVersions := make([]int, 0, len(vers)) 119 for version := range vers { 120 intVersions = append(intVersions, version) 121 } 122 sort.Ints(intVersions) 123 return Description{ 124 Name: name, 125 Versions: intVersions, 126 } 127 } 128 129 // List returns a slice describing each of the registered Facades. 130 func (f *Registry) List() []Description { 131 names := make([]string, 0, len(f.facades)) 132 for name := range f.facades { 133 names = append(names, name) 134 } 135 sort.Strings(names) 136 descriptions := make([]Description, 0, len(f.facades)) 137 for _, name := range names { 138 facades := f.facades[name] 139 description := descriptionFromVersions(name, facades) 140 if len(description.Versions) > 0 { 141 descriptions = append(descriptions, description) 142 } 143 } 144 return descriptions 145 } 146 147 // Details holds information about a facade. 148 type Details struct { 149 // Name is the name of the facade. 150 Name string 151 // Version holds the version of the facade. 152 Version int 153 // Factory holds the factory function for making 154 // instances of the facade. 155 Factory Factory 156 // Type holds the type of object that the Factory 157 // will return. This can be used to find out 158 // details of the facade without actually creating 159 // a facade instance (see rpcreflect.ObjTypeOf). 160 Type reflect.Type 161 } 162 163 // ListDetails returns information about all the facades 164 // registered in f, ordered lexically by name. 165 func (f *Registry) ListDetails() []Details { 166 names := make([]string, 0, len(f.facades)) 167 for name := range f.facades { 168 names = append(names, name) 169 } 170 sort.Strings(names) 171 var details []Details 172 for _, name := range names { 173 for v, info := range f.facades[name] { 174 details = append(details, Details{ 175 Name: name, 176 Version: v, 177 Factory: info.factory, 178 Type: info.facadeType, 179 }) 180 } 181 } 182 return details 183 } 184 185 // Discard gets rid of a registration that has already been done. Calling 186 // discard on an entry that is not present is not considered an error. 187 func (f *Registry) Discard(name string, version int) { 188 if versions, ok := f.facades[name]; ok { 189 delete(versions, version) 190 if len(versions) == 0 { 191 delete(f.facades, name) 192 } 193 } 194 }