109 lines
3.3 KiB
Go
109 lines
3.3 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
// Package factory contains a "factory" pattern based on argmapper.
|
|
//
|
|
// A Factory can be used to register factory methods to create some predefined
|
|
// type or interface implementation. These functions are argmapper functions so
|
|
// converters and so on can be used as part of instantiation.
|
|
package factory
|
|
|
|
import (
|
|
"fmt"
|
|
"reflect"
|
|
|
|
"github.com/hashicorp/go-argmapper"
|
|
"github.com/hashicorp/vagrant-plugin-sdk/internal-shared/dynamic"
|
|
)
|
|
|
|
// Factory keeps track of named dependency-injected factory functions to
|
|
// create an implementation of an interface.
|
|
type Factory struct {
|
|
iface reflect.Type
|
|
funcs map[string]*argmapper.Func
|
|
}
|
|
|
|
// New creates a Factory for the interface iface. The parameter
|
|
// iface should be a nil pointer to the interface type. Example: (*iface)(nil).
|
|
func New(iface interface{}) (*Factory, error) {
|
|
// Get the interface type
|
|
it := reflect.TypeOf(iface)
|
|
if k := it.Kind(); k != reflect.Ptr {
|
|
return nil, fmt.Errorf("iface must be a pointer to an interface, got %s", k)
|
|
}
|
|
it = it.Elem()
|
|
if k := it.Kind(); k != reflect.Interface {
|
|
return nil, fmt.Errorf("iface must be a pointer to an interface, got %s", k)
|
|
}
|
|
|
|
return &Factory{iface: it, funcs: make(map[string]*argmapper.Func)}, nil
|
|
}
|
|
|
|
// Register registers a factory function named name for the interface.
|
|
//
|
|
// This will error if the function given doesn't result in a single non-error
|
|
// output that implements the interface registered with this factory. The
|
|
// function return signature can be: (T) or (T, error) where T implements
|
|
// the interface type for this factory.
|
|
//
|
|
// T is allowed to be a literal interface{} type. In this case, it is the
|
|
// callers responsibility to ensure the result is the proper type.
|
|
//
|
|
// fn may take any number and types of inputs. It is the callers responsibilty
|
|
// when using Func and Call to pass in the required parameters.
|
|
func (f *Factory) Register(name string, fn interface{}) error {
|
|
ff, err := argmapper.NewFunc(fn,
|
|
argmapper.Logger(dynamic.Logger))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
outputs := ff.Output().Values()
|
|
if len(outputs) != 1 {
|
|
return fmt.Errorf("factory functions should have exactly one output: the implementation")
|
|
}
|
|
|
|
// We allow "interface{}" to pass through, in which case we trust that
|
|
// the callback is generating the correct type.
|
|
typ := outputs[0].Type
|
|
if typ != ifaceType && !typ.Implements(f.iface) {
|
|
return fmt.Errorf("factory output should implement interface: %s", f.iface)
|
|
}
|
|
|
|
f.funcs[name] = ff
|
|
return nil
|
|
}
|
|
|
|
// Func returns the factory function named name. This can then be used to
|
|
// call and instantiate the factory interface type.
|
|
func (f *Factory) Func(name string) *argmapper.Func {
|
|
return f.funcs[name]
|
|
}
|
|
|
|
// Registered returns the names registered with this factory.
|
|
func (f *Factory) Registered() []string {
|
|
result := make([]string, 0, len(f.funcs))
|
|
for k := range f.funcs {
|
|
result = append(result, k)
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
// Copy returns a copy of Factory. Any registrations on the copy will not
|
|
// reflect the original and vice versa.
|
|
func (f *Factory) Copy() *Factory {
|
|
// Copy
|
|
f2 := *f
|
|
|
|
// Build new funcs
|
|
f2.funcs = map[string]*argmapper.Func{}
|
|
for k, v := range f.funcs {
|
|
f2.funcs[k] = v
|
|
}
|
|
|
|
return &f2
|
|
}
|
|
|
|
var ifaceType = reflect.TypeOf((*interface{})(nil)).Elem()
|