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  }